From 6ddf8c5235dd2e5b63e0989d938aa3a3be2c09f4 Mon Sep 17 00:00:00 2001 From: brijesh-elastic Date: Tue, 16 Apr 2024 19:08:49 +0530 Subject: [PATCH 1/8] Add support of case connector for thehive --- .../cases/common/types/domain/connector/v1.ts | 19 +++++ .../public/components/connectors/index.ts | 3 + .../public/components/connectors/mock.ts | 10 +++ .../connectors/thehive/case_fields.test.tsx | 50 +++++++++++++ .../connectors/thehive/case_fields.tsx | 72 +++++++++++++++++++ .../thehive/case_fields_preview.test.tsx | 34 +++++++++ .../thehive/case_fields_preview.tsx | 63 ++++++++++++++++ .../components/connectors/thehive/index.ts | 20 ++++++ .../connectors/thehive/translations.ts | 23 ++++++ .../components/connectors/thehive/types.ts | 14 ++++ .../server/client/user_actions/connectors.ts | 22 +++--- .../cases/server/connectors/factory.ts | 2 + .../server/connectors/thehive/format.test.ts | 28 ++++++++ .../cases/server/connectors/thehive/format.ts | 35 +++++++++ .../cases/server/connectors/thehive/index.ts | 15 ++++ .../server/connectors/thehive/mapping.ts | 33 +++++++++ .../cases/server/connectors/thehive/types.ts | 18 +++++ 17 files changed, 451 insertions(+), 10 deletions(-) create mode 100644 x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx create mode 100644 x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx create mode 100644 x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.test.tsx create mode 100644 x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.tsx create mode 100644 x-pack/plugins/cases/public/components/connectors/thehive/index.ts create mode 100644 x-pack/plugins/cases/public/components/connectors/thehive/translations.ts create mode 100644 x-pack/plugins/cases/public/components/connectors/thehive/types.ts create mode 100644 x-pack/plugins/cases/server/connectors/thehive/format.test.ts create mode 100644 x-pack/plugins/cases/server/connectors/thehive/format.ts create mode 100644 x-pack/plugins/cases/server/connectors/thehive/index.ts create mode 100644 x-pack/plugins/cases/server/connectors/thehive/mapping.ts create mode 100644 x-pack/plugins/cases/server/connectors/thehive/types.ts diff --git a/x-pack/plugins/cases/common/types/domain/connector/v1.ts b/x-pack/plugins/cases/common/types/domain/connector/v1.ts index a690abcab97843..9c6a2d3cfff779 100644 --- a/x-pack/plugins/cases/common/types/domain/connector/v1.ts +++ b/x-pack/plugins/cases/common/types/domain/connector/v1.ts @@ -21,6 +21,7 @@ export enum ConnectorTypes { serviceNowITSM = '.servicenow', serviceNowSIR = '.servicenow-sir', swimlane = '.swimlane', + theHive = '.thehive', } const ConnectorCasesWebhookTypeFieldsRt = rt.strict({ @@ -118,6 +119,21 @@ const ConnectorSwimlaneTypeFieldsRt = rt.strict({ fields: rt.union([SwimlaneFieldsRt, rt.null]), }); +/** + * Thehive + */ + +export const TheHiveFieldsRt = rt.strict({ + tlp: rt.union([rt.number, rt.string, rt.null]), +}); + +export type TheHiveFieldsType = rt.TypeOf; + +const ConnectorTheHiveTypeFieldsRt = rt.strict({ + type: rt.literal(ConnectorTypes.theHive), + fields: rt.union([TheHiveFieldsRt, rt.null]), +}); + /** * None connector */ @@ -135,6 +151,7 @@ export const ConnectorTypeFieldsRt = rt.union([ ConnectorServiceNowITSMTypeFieldsRt, ConnectorServiceNowSIRTypeFieldsRt, ConnectorSwimlaneTypeFieldsRt, + ConnectorTheHiveTypeFieldsRt, ]); /** @@ -148,6 +165,7 @@ export const CaseUserActionConnectorRt = rt.union([ rt.intersection([ConnectorServiceNowITSMTypeFieldsRt, rt.strict({ name: rt.string })]), rt.intersection([ConnectorServiceNowSIRTypeFieldsRt, rt.strict({ name: rt.string })]), rt.intersection([ConnectorSwimlaneTypeFieldsRt, rt.strict({ name: rt.string })]), + rt.intersection([ConnectorTheHiveTypeFieldsRt, rt.strict({ name: rt.string })]), ]); export const CaseConnectorRt = rt.intersection([ @@ -205,3 +223,4 @@ export type ConnectorServiceNowITSMTypeFields = rt.TypeOf< typeof ConnectorServiceNowITSMTypeFieldsRt >; export type ConnectorServiceNowSIRTypeFields = rt.TypeOf; +export type ConnectorTheHiveTypeFields = rt.TypeOf; diff --git a/x-pack/plugins/cases/public/components/connectors/index.ts b/x-pack/plugins/cases/public/components/connectors/index.ts index b3ba36f75d65ca..66681b8dd547ac 100644 --- a/x-pack/plugins/cases/public/components/connectors/index.ts +++ b/x-pack/plugins/cases/public/components/connectors/index.ts @@ -12,12 +12,14 @@ import { getCaseConnector as getSwimlaneCaseConnector } from './swimlane'; import { getCaseConnector as getResilientCaseConnector } from './resilient'; import { getCaseConnector as getCasesWebhookCaseConnector } from './cases_webhook'; import { getServiceNowITSMCaseConnector, getServiceNowSIRCaseConnector } from './servicenow'; +import { getCaseConnector as getTheHiveCaseConnector } from './thehive'; import type { JiraFieldsType, ServiceNowITSMFieldsType, ServiceNowSIRFieldsType, ResilientFieldsType, SwimlaneFieldsType, + TheHiveFieldsType, } from '../../../common/types/domain'; export * from './types'; @@ -43,6 +45,7 @@ class CaseConnectors { this.caseConnectorsRegistry.register(getServiceNowSIRCaseConnector()); this.caseConnectorsRegistry.register(getSwimlaneCaseConnector()); this.caseConnectorsRegistry.register(getCasesWebhookCaseConnector()); + this.caseConnectorsRegistry.register(getTheHiveCaseConnector()); } registry(): CaseConnectorsRegistry { diff --git a/x-pack/plugins/cases/public/components/connectors/mock.ts b/x-pack/plugins/cases/public/components/connectors/mock.ts index b7271d40043d3d..b5f2ccd5c680a3 100644 --- a/x-pack/plugins/cases/public/components/connectors/mock.ts +++ b/x-pack/plugins/cases/public/components/connectors/mock.ts @@ -35,6 +35,16 @@ export const swimlaneConnector = { isSystemAction: false, }; +export const theHiveConnector = { + id: '123', + name: 'My connector', + actionTypeId: '.thehive', + config: {}, + isPreconfigured: false, + isDeprecated: false, + isSystemAction: false, +}; + export const issues = [ { id: 'personId', title: 'Person Task', key: 'personKey' }, { id: 'womanId', title: 'Woman Task', key: 'womanKey' }, diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx new file mode 100644 index 00000000000000..7fd2764029fe46 --- /dev/null +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import userEvent from '@testing-library/user-event'; +import { screen } from '@testing-library/react'; +import Fields from './case_fields'; +import { theHiveConnector as connector } from '../mock'; +import { MockFormWrapperComponent } from '../test_utils'; +import type { AppMockRenderer } from '../../../common/mock'; +import { createAppMockRenderer } from '../../../common/mock'; +import { TheHiveTLP } from './types'; + +describe('TheHive Cases Fields', () => { + const fields = { + TLP: '1', + }; + + let appMockRenderer: AppMockRenderer; + + beforeEach(() => { + appMockRenderer = createAppMockRenderer(); + jest.clearAllMocks(); + }); + + it('all params fields are rendered', () => { + appMockRenderer.render( + + + + ); + + expect(screen.getByText('TLP')).toBeInTheDocument(); + }); + + it('sets TLP correctly', async () => { + appMockRenderer.render( + + + + ); + + userEvent.selectOptions(screen.getByTestId('tlp-field'), '4'); + expect(await screen.findByTestId('tlp-field')).toHaveValue(TheHiveTLP.RED); + }); +}); \ No newline at end of file diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx new file mode 100644 index 00000000000000..1526509be4c685 --- /dev/null +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { SelectField } from '@kbn/es-ui-shared-plugin/static/forms/components'; +import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; +import type { ConnectorFieldsProps } from '../types'; +import * as i18n from './translations'; +import { TheHiveTLP } from './types'; + +const { emptyField } = fieldValidators; + +const TheHiveFieldsComponent: React.FunctionComponent = () => { + + const tlpOptions: Array<{ text: string, value: string }> = [ + { + text: 'CLEAR', + value: TheHiveTLP.CLEAR + }, + { + text: 'GREEN', + value: TheHiveTLP.GREEN + }, + { + text: 'AMBER', + value: TheHiveTLP.AMBER + }, + { + text: 'AMBER+STRICT', + value: TheHiveTLP.AMBER_STRICT + }, + { + text: 'RED', + value: TheHiveTLP.RED + } + ]; + + return ( +
+ +
+ ); +}; + +TheHiveFieldsComponent.displayName = 'ThehiveFields'; + +// eslint-disable-next-line import/no-default-export +export { TheHiveFieldsComponent as default }; diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.test.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.test.tsx new file mode 100644 index 00000000000000..0c4e43f07efb59 --- /dev/null +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.test.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { screen } from '@testing-library/react'; +import FieldsPreview from './case_fields_preview'; +import type { AppMockRenderer } from '../../../common/mock'; +import { theHiveConnector } from '../mock'; +import { createAppMockRenderer } from '../../../common/mock'; +import { createQueryWithMarkup } from '../../../common/test_utils'; + +describe('TheHive Fields: Preview', () => { + const fields = { + tlp: '1', + }; + + let appMockRenderer: AppMockRenderer; + + beforeEach(() => { + appMockRenderer = createAppMockRenderer(); + jest.clearAllMocks(); + }); + + it('renders all fields correctly', () => { + appMockRenderer.render(); + + const getByText = createQueryWithMarkup(screen.getByText); + expect(getByText('TLP: GREEN')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.tsx new file mode 100644 index 00000000000000..36640cbac5a016 --- /dev/null +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; + +import type { TheHiveFieldsType } from '../../../../common/types/domain'; +import { ConnectorTypes } from '../../../../common/types/domain'; +import type { ConnectorFieldsPreviewProps } from '../types'; +import { ConnectorCard } from '../card'; +import * as i18n from './translations'; + +const TheHiveFieldsPreviewComponent: React.FunctionComponent< + ConnectorFieldsPreviewProps +> = ({ fields, connector }) => { + const { tlp } = fields ?? {}; + + const mapTLP = (tlp: number | string): string => { + switch (tlp) { + case "0": + return 'CLEAR'; + case "1": + return 'GREEN'; + case "2": + return 'AMBER'; + case "3": + return 'AMBER+STRICT'; + case "4": + return 'RED'; + default: + return 'AMBER'; + } + } + + const listItems = useMemo( + () => [ + ...(tlp !== null ? [ + { + title: i18n.TLP_LABEL, + description: mapTLP(tlp), + }, + ] : []), + ], + [tlp] + ); + + return ( + + ); +}; + +TheHiveFieldsPreviewComponent.displayName = 'TheHiveFieldsPreview'; + +// eslint-disable-next-line import/no-default-export +export { TheHiveFieldsPreviewComponent as default }; diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/index.ts b/x-pack/plugins/cases/public/components/connectors/thehive/index.ts new file mode 100644 index 00000000000000..ad80df5f0eb492 --- /dev/null +++ b/x-pack/plugins/cases/public/components/connectors/thehive/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { lazy } from 'react'; + +import type { CaseConnector } from '../types'; +import type { TheHiveFieldsType } from '../../../../common/types/domain'; +import { ConnectorTypes } from '../../../../common/types/domain'; + +export * from './types'; + +export const getCaseConnector = (): CaseConnector => ({ + id: ConnectorTypes.theHive, + fieldsComponent: lazy(() => import('./case_fields')), + previewComponent: lazy(() => import('./case_fields_preview')), +}); diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/translations.ts b/x-pack/plugins/cases/public/components/connectors/thehive/translations.ts new file mode 100644 index 00000000000000..31121c97e6daef --- /dev/null +++ b/x-pack/plugins/cases/public/components/connectors/thehive/translations.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + + +export const TLP_LABEL = i18n.translate( + 'xpack.cases.connectors.thehive.tlpLable', + { + defaultMessage: 'TLP', + } +); + +export const TLP_REQUIRED = i18n.translate( + 'xpack.cases.connectors.thehive.tlpLableRequired', + { + defaultMessage: 'TLP is required', + } +); \ No newline at end of file diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/types.ts b/x-pack/plugins/cases/public/components/connectors/thehive/types.ts new file mode 100644 index 00000000000000..8235e21dce67b4 --- /dev/null +++ b/x-pack/plugins/cases/public/components/connectors/thehive/types.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export enum TheHiveTLP { + CLEAR = "0", + GREEN = "1", + AMBER = "2", + AMBER_STRICT = "3", + RED = "4" +} \ No newline at end of file diff --git a/x-pack/plugins/cases/server/client/user_actions/connectors.ts b/x-pack/plugins/cases/server/client/user_actions/connectors.ts index c9ebfe8724a917..772cf7c63f5726 100644 --- a/x-pack/plugins/cases/server/client/user_actions/connectors.ts +++ b/x-pack/plugins/cases/server/client/user_actions/connectors.ts @@ -148,14 +148,16 @@ const getConnectorsInfo = async ({ * the severity user actions or if there is a mechanism to * define supported user actions per connector type */ - const hasCasesWebhookConnector = actionConnectors.some( - (actionConnector) => actionConnector.actionTypeId === ConnectorTypes.casesWebhook + const hasAdditionalUserActionsConnector = actionConnectors.some( + (actionConnector) => + actionConnector.actionTypeId === ConnectorTypes.casesWebhook || + actionConnector.actionTypeId === ConnectorTypes.theHive ); - let latestUserActionCasesWebhook: SavedObject | undefined; - if (hasCasesWebhookConnector) { + let latestAdditionalUserActionConnector: SavedObject | undefined; + if (hasAdditionalUserActionsConnector) { // if cases webhook connector, we need to fetch latestUserAction again because // the cases webhook connector includes extra fields other case connectors do not track - latestUserActionCasesWebhook = await userActionService.getMostRecentUserAction(caseId, true); + latestAdditionalUserActionConnector = await userActionService.getMostRecentUserAction(caseId, true); } return createConnectorInfoResult({ @@ -163,7 +165,7 @@ const getConnectorsInfo = async ({ connectors, pushInfo, latestUserAction, - latestUserActionCasesWebhook, + latestAdditionalUserActionConnector, }); }; @@ -289,13 +291,13 @@ const createConnectorInfoResult = ({ connectors, pushInfo, latestUserAction, - latestUserActionCasesWebhook, + latestAdditionalUserActionConnector, }: { actionConnectors: ActionResult[]; connectors: CaseConnectorActivity[]; pushInfo: Map; latestUserAction?: SavedObject; - latestUserActionCasesWebhook?: SavedObject; + latestAdditionalUserActionConnector?: SavedObject; }) => { const results: GetCaseConnectorsResponse = {}; const actionConnectorsMap = new Map( @@ -312,8 +314,8 @@ const createConnectorInfoResult = ({ * the severity user actions or if there is a mechanism to * define supported user actions per connector type */ - connectorDetails?.actionTypeId === ConnectorTypes.casesWebhook - ? latestUserActionCasesWebhook?.attributes.created_at + connectorDetails?.actionTypeId === ConnectorTypes.casesWebhook || connectorDetails?.actionTypeId === ConnectorTypes.theHive + ? latestAdditionalUserActionConnector?.attributes.created_at : latestUserAction?.attributes.created_at ); diff --git a/x-pack/plugins/cases/server/connectors/factory.ts b/x-pack/plugins/cases/server/connectors/factory.ts index 8cded028be2640..8a5fb3d2fcfa76 100644 --- a/x-pack/plugins/cases/server/connectors/factory.ts +++ b/x-pack/plugins/cases/server/connectors/factory.ts @@ -12,6 +12,7 @@ import { getCaseConnector as getResilientCaseConnector } from './resilient'; import { getCaseConnector as getCasesWebhookCaseConnector } from './cases_webook'; import { getServiceNowITSMCaseConnector, getServiceNowSIRCaseConnector } from './servicenow'; import { getCaseConnector as getSwimlaneCaseConnector } from './swimlane'; +import { getCaseConnector as getTheHiveCaseConnector } from './thehive'; const mapping: Record = { [ConnectorTypes.casesWebhook]: getCasesWebhookCaseConnector(), @@ -20,6 +21,7 @@ const mapping: Record = { [ConnectorTypes.serviceNowSIR]: getServiceNowSIRCaseConnector(), [ConnectorTypes.resilient]: getResilientCaseConnector(), [ConnectorTypes.swimlane]: getSwimlaneCaseConnector(), + [ConnectorTypes.theHive]: getTheHiveCaseConnector(), [ConnectorTypes.none]: null, }; diff --git a/x-pack/plugins/cases/server/connectors/thehive/format.test.ts b/x-pack/plugins/cases/server/connectors/thehive/format.test.ts new file mode 100644 index 00000000000000..5915c7b0bdb3df --- /dev/null +++ b/x-pack/plugins/cases/server/connectors/thehive/format.test.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Case } from '../../../common/types/domain'; +import { format } from './format'; + +describe('TheHive formatter', () => { + const theCase = { + tags: ['tag1'], + severity: 'high', + connector: { fields: { tlp: '1' } }, + } as Case; + + it('it formats correctly', async () => { + const res = await format(theCase, []); + expect(res).toEqual({ tlp: 1, tags: ['tag1'], severity: 3 }); + }); + + it('it formats correctly when fields do not exist ', async () => { + const invalidFields = { tags: ['tag1'], severity: 'low', connector: { fields: null } } as Case; + const res = await format(invalidFields, []); + expect(res).toEqual({ tlp: null, severity: 1, tags: ['tag1'] }); + }); +}); \ No newline at end of file diff --git a/x-pack/plugins/cases/server/connectors/thehive/format.ts b/x-pack/plugins/cases/server/connectors/thehive/format.ts new file mode 100644 index 00000000000000..b51e8e6c27049e --- /dev/null +++ b/x-pack/plugins/cases/server/connectors/thehive/format.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Format } from './types'; +import type { ConnectorTheHiveTypeFields} from '../../../common/types/domain'; + +function mapSeverity(severity: string): number{ + switch (severity) { + case 'low': + return 1; + case 'medium': + return 2; + case 'high': + return 3; + case 'critical': + return 4; + default: + return 2; + } +} + +export const format: Format = (theCase) => { + const { + tlp = null + } = (theCase.connector.fields as ConnectorTheHiveTypeFields['fields']) ?? {}; + return { + tags: theCase.tags, + tlp: typeof tlp === 'string' ? parseInt(tlp) : tlp, + severity: mapSeverity(theCase.severity), + }; +}; diff --git a/x-pack/plugins/cases/server/connectors/thehive/index.ts b/x-pack/plugins/cases/server/connectors/thehive/index.ts new file mode 100644 index 00000000000000..99491c024aa723 --- /dev/null +++ b/x-pack/plugins/cases/server/connectors/thehive/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getMapping } from './mapping'; +import { format } from './format'; +import type {TheHiveCaseConnector } from './types'; + +export const getCaseConnector = (): TheHiveCaseConnector => ({ + getMapping, + format, +}); diff --git a/x-pack/plugins/cases/server/connectors/thehive/mapping.ts b/x-pack/plugins/cases/server/connectors/thehive/mapping.ts new file mode 100644 index 00000000000000..d0ccd7ce7c197c --- /dev/null +++ b/x-pack/plugins/cases/server/connectors/thehive/mapping.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { GetMapping } from './types'; + +export const getMapping: GetMapping = () => { + return [ + { + source: 'title', + target: 'title', + action_type: 'overwrite', + }, + { + source: 'description', + target: 'description', + action_type: 'overwrite', + }, + { + source: 'tags', + target: 'tags', + action_type: 'overwrite', + }, + { + source: 'comments', + target: 'comments', + action_type: 'append', + }, + ]; +}; diff --git a/x-pack/plugins/cases/server/connectors/thehive/types.ts b/x-pack/plugins/cases/server/connectors/thehive/types.ts new file mode 100644 index 00000000000000..4e36bea761f6b1 --- /dev/null +++ b/x-pack/plugins/cases/server/connectors/thehive/types.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { TheHiveFieldsType } from '../../../common/types/domain'; +import type { ICasesConnector } from '../types'; + +interface ExternalServiceFormatterParams extends TheHiveFieldsType { + tags: string[]; + severity: number; +} + +export type TheHiveCaseConnector = ICasesConnector; +export type Format = ICasesConnector['format']; +export type GetMapping = ICasesConnector['getMapping']; From 19b81b22db30a62583647d904105de9ac48e6a16 Mon Sep 17 00:00:00 2001 From: brijesh-elastic Date: Tue, 27 Aug 2024 15:48:35 +0530 Subject: [PATCH 2/8] Resolve comments --- .../connectors/thehive/case_fields.tsx | 47 +++++++++--------- .../thehive/case_fields_preview.tsx | 48 ++++++++++--------- .../cases/server/connectors/thehive/format.ts | 10 ++-- .../server/connector_types/thehive/index.ts | 2 + 4 files changed, 54 insertions(+), 53 deletions(-) diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx index 1526509be4c685..08ac059c1e5ecb 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx @@ -15,31 +15,30 @@ import { TheHiveTLP } from './types'; const { emptyField } = fieldValidators; -const TheHiveFieldsComponent: React.FunctionComponent = () => { - - const tlpOptions: Array<{ text: string, value: string }> = [ - { - text: 'CLEAR', - value: TheHiveTLP.CLEAR - }, - { - text: 'GREEN', - value: TheHiveTLP.GREEN - }, - { - text: 'AMBER', - value: TheHiveTLP.AMBER - }, - { - text: 'AMBER+STRICT', - value: TheHiveTLP.AMBER_STRICT - }, - { - text: 'RED', - value: TheHiveTLP.RED - } - ]; +const tlpOptions: Array<{ text: string; value: string }> = [ + { + text: 'CLEAR', + value: TheHiveTLP.CLEAR, + }, + { + text: 'GREEN', + value: TheHiveTLP.GREEN, + }, + { + text: 'AMBER', + value: TheHiveTLP.AMBER, + }, + { + text: 'AMBER+STRICT', + value: TheHiveTLP.AMBER_STRICT, + }, + { + text: 'RED', + value: TheHiveTLP.RED, + }, +]; +const TheHiveFieldsComponent: React.FunctionComponent = () => { return (
{ + switch (tlpValue) { + case '0': + return 'CLEAR'; + case '1': + return 'GREEN'; + case '2': + return 'AMBER'; + case '3': + return 'AMBER+STRICT'; + case '4': + return 'RED'; + default: + return 'AMBER'; + } +}; + const TheHiveFieldsPreviewComponent: React.FunctionComponent< ConnectorFieldsPreviewProps > = ({ fields, connector }) => { const { tlp } = fields ?? {}; - const mapTLP = (tlp: number | string): string => { - switch (tlp) { - case "0": - return 'CLEAR'; - case "1": - return 'GREEN'; - case "2": - return 'AMBER'; - case "3": - return 'AMBER+STRICT'; - case "4": - return 'RED'; - default: - return 'AMBER'; - } - } - const listItems = useMemo( () => [ - ...(tlp !== null ? [ - { - title: i18n.TLP_LABEL, - description: mapTLP(tlp), - }, - ] : []), + ...(tlp !== null + ? [ + { + title: i18n.TLP_LABEL, + description: mapTLP(tlp), + }, + ] + : []), ], [tlp] ); diff --git a/x-pack/plugins/cases/server/connectors/thehive/format.ts b/x-pack/plugins/cases/server/connectors/thehive/format.ts index b51e8e6c27049e..8dd9fa7c191b9f 100644 --- a/x-pack/plugins/cases/server/connectors/thehive/format.ts +++ b/x-pack/plugins/cases/server/connectors/thehive/format.ts @@ -6,9 +6,9 @@ */ import type { Format } from './types'; -import type { ConnectorTheHiveTypeFields} from '../../../common/types/domain'; +import type { ConnectorTheHiveTypeFields } from '../../../common/types/domain'; -function mapSeverity(severity: string): number{ +function mapSeverity(severity: string): number { switch (severity) { case 'low': return 1; @@ -24,12 +24,10 @@ function mapSeverity(severity: string): number{ } export const format: Format = (theCase) => { - const { - tlp = null - } = (theCase.connector.fields as ConnectorTheHiveTypeFields['fields']) ?? {}; + const { tlp = null } = (theCase.connector.fields as ConnectorTheHiveTypeFields['fields']) ?? {}; return { tags: theCase.tags, - tlp: typeof tlp === 'string' ? parseInt(tlp) : tlp, + tlp: typeof tlp === 'string' ? parseInt(tlp, 10) : tlp, severity: mapSeverity(theCase.severity), }; }; diff --git a/x-pack/plugins/stack_connectors/server/connector_types/thehive/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/thehive/index.ts index d39849adb44904..50860dd8a9fab9 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/thehive/index.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/thehive/index.ts @@ -13,6 +13,7 @@ import { AlertingConnectorFeatureId, SecurityConnectorFeatureId, UptimeConnectorFeatureId, + CasesConnectorFeatureId, } from '@kbn/actions-plugin/common/types'; import { urlAllowListValidator } from '@kbn/actions-plugin/server'; import { TheHiveConnector } from './thehive'; @@ -36,6 +37,7 @@ export function getConnectorType(): TheHiveConnectorType { AlertingConnectorFeatureId, SecurityConnectorFeatureId, UptimeConnectorFeatureId, + CasesConnectorFeatureId, ], schema: { config: TheHiveConfigSchema, From c5ba79a99ab35aaee7e48efc68ce3fce935b8796 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 28 Aug 2024 09:32:32 +0000 Subject: [PATCH 3/8] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../connectors/thehive/case_fields.test.tsx | 2 +- .../connectors/thehive/translations.ts | 19 ++++++------------- .../components/connectors/thehive/types.ts | 12 ++++++------ .../server/client/user_actions/connectors.ts | 8 ++++++-- .../server/connectors/thehive/format.test.ts | 2 +- .../cases/server/connectors/thehive/index.ts | 2 +- 6 files changed, 21 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx index 7fd2764029fe46..4ada4695ad97b1 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx @@ -47,4 +47,4 @@ describe('TheHive Cases Fields', () => { userEvent.selectOptions(screen.getByTestId('tlp-field'), '4'); expect(await screen.findByTestId('tlp-field')).toHaveValue(TheHiveTLP.RED); }); -}); \ No newline at end of file +}); diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/translations.ts b/x-pack/plugins/cases/public/components/connectors/thehive/translations.ts index 31121c97e6daef..30977b1b73a6fc 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/translations.ts +++ b/x-pack/plugins/cases/public/components/connectors/thehive/translations.ts @@ -7,17 +7,10 @@ import { i18n } from '@kbn/i18n'; +export const TLP_LABEL = i18n.translate('xpack.cases.connectors.thehive.tlpLable', { + defaultMessage: 'TLP', +}); -export const TLP_LABEL = i18n.translate( - 'xpack.cases.connectors.thehive.tlpLable', - { - defaultMessage: 'TLP', - } -); - -export const TLP_REQUIRED = i18n.translate( - 'xpack.cases.connectors.thehive.tlpLableRequired', - { - defaultMessage: 'TLP is required', - } -); \ No newline at end of file +export const TLP_REQUIRED = i18n.translate('xpack.cases.connectors.thehive.tlpLableRequired', { + defaultMessage: 'TLP is required', +}); diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/types.ts b/x-pack/plugins/cases/public/components/connectors/thehive/types.ts index 8235e21dce67b4..78c4dce8b52ba8 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/types.ts +++ b/x-pack/plugins/cases/public/components/connectors/thehive/types.ts @@ -6,9 +6,9 @@ */ export enum TheHiveTLP { - CLEAR = "0", - GREEN = "1", - AMBER = "2", - AMBER_STRICT = "3", - RED = "4" -} \ No newline at end of file + CLEAR = '0', + GREEN = '1', + AMBER = '2', + AMBER_STRICT = '3', + RED = '4', +} diff --git a/x-pack/plugins/cases/server/client/user_actions/connectors.ts b/x-pack/plugins/cases/server/client/user_actions/connectors.ts index 772cf7c63f5726..74fbd404c3cc3e 100644 --- a/x-pack/plugins/cases/server/client/user_actions/connectors.ts +++ b/x-pack/plugins/cases/server/client/user_actions/connectors.ts @@ -157,7 +157,10 @@ const getConnectorsInfo = async ({ if (hasAdditionalUserActionsConnector) { // if cases webhook connector, we need to fetch latestUserAction again because // the cases webhook connector includes extra fields other case connectors do not track - latestAdditionalUserActionConnector = await userActionService.getMostRecentUserAction(caseId, true); + latestAdditionalUserActionConnector = await userActionService.getMostRecentUserAction( + caseId, + true + ); } return createConnectorInfoResult({ @@ -314,7 +317,8 @@ const createConnectorInfoResult = ({ * the severity user actions or if there is a mechanism to * define supported user actions per connector type */ - connectorDetails?.actionTypeId === ConnectorTypes.casesWebhook || connectorDetails?.actionTypeId === ConnectorTypes.theHive + connectorDetails?.actionTypeId === ConnectorTypes.casesWebhook || + connectorDetails?.actionTypeId === ConnectorTypes.theHive ? latestAdditionalUserActionConnector?.attributes.created_at : latestUserAction?.attributes.created_at ); diff --git a/x-pack/plugins/cases/server/connectors/thehive/format.test.ts b/x-pack/plugins/cases/server/connectors/thehive/format.test.ts index 5915c7b0bdb3df..eeced683cf393a 100644 --- a/x-pack/plugins/cases/server/connectors/thehive/format.test.ts +++ b/x-pack/plugins/cases/server/connectors/thehive/format.test.ts @@ -25,4 +25,4 @@ describe('TheHive formatter', () => { const res = await format(invalidFields, []); expect(res).toEqual({ tlp: null, severity: 1, tags: ['tag1'] }); }); -}); \ No newline at end of file +}); diff --git a/x-pack/plugins/cases/server/connectors/thehive/index.ts b/x-pack/plugins/cases/server/connectors/thehive/index.ts index 99491c024aa723..2578c06d399521 100644 --- a/x-pack/plugins/cases/server/connectors/thehive/index.ts +++ b/x-pack/plugins/cases/server/connectors/thehive/index.ts @@ -7,7 +7,7 @@ import { getMapping } from './mapping'; import { format } from './format'; -import type {TheHiveCaseConnector } from './types'; +import type { TheHiveCaseConnector } from './types'; export const getCaseConnector = (): TheHiveCaseConnector => ({ getMapping, From c58a5787fbcba8443601a5a46c7ecaeb5b71e61c Mon Sep 17 00:00:00 2001 From: brijesh-elastic Date: Thu, 29 Aug 2024 17:28:44 +0530 Subject: [PATCH 4/8] Update docs --- docs/management/action-types.asciidoc | 4 ++-- .../connectors/action-types/thehive.asciidoc | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/docs/management/action-types.asciidoc b/docs/management/action-types.asciidoc index da58ff6f295b00..a39b5a996dca35 100644 --- a/docs/management/action-types.asciidoc +++ b/docs/management/action-types.asciidoc @@ -92,9 +92,9 @@ a| <> | Create an incident in {swimlane}. -a| <> +a| <> -| Create cases and alerts in {thehive}. +| Create cases and alerts in TheHive. a| <> diff --git a/docs/management/connectors/action-types/thehive.asciidoc b/docs/management/connectors/action-types/thehive.asciidoc index 302bd142031eee..3252a1a5830d8a 100644 --- a/docs/management/connectors/action-types/thehive.asciidoc +++ b/docs/management/connectors/action-types/thehive.asciidoc @@ -33,10 +33,11 @@ URL:: TheHive instance URL. API Key:: TheHive API key for authentication. [float] -[[TheHive-action-configuration]] +[[thehive-action-configuration]] === Test connectors -You can test connectors as you're creating or editing the connector in {kib}. For example: +You can test connectors for creating a case or an alert with the <> or +as you're creating or editing the connector in {kib}. For example: [role="screenshot"] image::management/connectors/images/thehive-params-case-test.png[TheHive case params test] @@ -54,11 +55,23 @@ Description:: The details about the incident. Severity:: Severity of the incident. This can be one of `LOW`, `MEDIUM`(default), `HIGH` or `CRITICAL`. TLP:: Traffic Light Protocol designation for the incident. This can be one of `CLEAR`, `GREEN`, `AMBER`(default), `AMBER+STRICT` or `RED`. Tags:: The keywords or tags about the incident. -Additional comments:: Additional information about the Case. +Additional comments:: Additional information about the Case. Type:: Type of the Alert. Source:: Source of the Alert. Source Reference:: Source reference of the Alert. +[float] +[[thehive-features]] +=== Features + +1. Rule base creation of alerts and cases. +2. Create case, Update case. + +[NOTE] +==== +* For update case, status of the case is not sync with the kibana case. +==== + [float] [[thehive-connector-networking-configuration]] === Connector networking configuration From db3d8402db77e42bb90e3eae7c22c6b4d9bd1d92 Mon Sep 17 00:00:00 2001 From: brijesh-elastic Date: Mon, 2 Sep 2024 15:17:13 +0530 Subject: [PATCH 5/8] Remove the hideInUi flag --- .../stack_connectors/public/connector_types/thehive/thehive.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/stack_connectors/public/connector_types/thehive/thehive.tsx b/x-pack/plugins/stack_connectors/public/connector_types/thehive/thehive.tsx index 5523a24e05d505..d52d2783e707b6 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/thehive/thehive.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/thehive/thehive.tsx @@ -24,7 +24,6 @@ export function getConnectorType(): TheHiveConnector { defaultMessage: 'Create cases and alerts in TheHive', }), actionTypeTitle: THEHIVE_TITLE, - hideInUi: true, validateParams: async ( actionParams: ExecutorParams ): Promise> => { From 1aa96496fdc8d92a4917cf2449d4d16b6c23ee21 Mon Sep 17 00:00:00 2001 From: brijesh-elastic Date: Mon, 2 Sep 2024 15:51:36 +0530 Subject: [PATCH 6/8] Adding back hideInUi flag --- .../stack_connectors/public/connector_types/thehive/thehive.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/stack_connectors/public/connector_types/thehive/thehive.tsx b/x-pack/plugins/stack_connectors/public/connector_types/thehive/thehive.tsx index d52d2783e707b6..5523a24e05d505 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/thehive/thehive.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/thehive/thehive.tsx @@ -24,6 +24,7 @@ export function getConnectorType(): TheHiveConnector { defaultMessage: 'Create cases and alerts in TheHive', }), actionTypeTitle: THEHIVE_TITLE, + hideInUi: true, validateParams: async ( actionParams: ExecutorParams ): Promise> => { From bf5598dd75919733fabaf7db08a7ed930d02e3d0 Mon Sep 17 00:00:00 2001 From: brijesh-elastic Date: Mon, 9 Sep 2024 19:12:41 +0530 Subject: [PATCH 7/8] Resolve comments --- .../cases/common/types/domain/connector/v1.ts | 2 +- .../connectors/thehive/case_fields.test.tsx | 6 ++- .../connectors/thehive/case_fields.tsx | 39 +++++++------------ .../thehive/case_fields_preview.test.tsx | 2 +- .../thehive/case_fields_preview.tsx | 19 ++------- .../components/connectors/thehive/types.ts | 10 ++--- .../server/connectors/thehive/format.test.ts | 2 +- .../cases/server/connectors/thehive/format.ts | 2 +- 8 files changed, 32 insertions(+), 50 deletions(-) diff --git a/x-pack/plugins/cases/common/types/domain/connector/v1.ts b/x-pack/plugins/cases/common/types/domain/connector/v1.ts index 9c6a2d3cfff779..9d21f034d63977 100644 --- a/x-pack/plugins/cases/common/types/domain/connector/v1.ts +++ b/x-pack/plugins/cases/common/types/domain/connector/v1.ts @@ -124,7 +124,7 @@ const ConnectorSwimlaneTypeFieldsRt = rt.strict({ */ export const TheHiveFieldsRt = rt.strict({ - tlp: rt.union([rt.number, rt.string, rt.null]), + tlp: rt.union([rt.number, rt.null]), }); export type TheHiveFieldsType = rt.TypeOf; diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx index 4ada4695ad97b1..13653e3301107f 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx @@ -17,7 +17,7 @@ import { TheHiveTLP } from './types'; describe('TheHive Cases Fields', () => { const fields = { - TLP: '1', + TLP: 1, }; let appMockRenderer: AppMockRenderer; @@ -45,6 +45,8 @@ describe('TheHive Cases Fields', () => { ); userEvent.selectOptions(screen.getByTestId('tlp-field'), '4'); - expect(await screen.findByTestId('tlp-field')).toHaveValue(TheHiveTLP.RED); + expect(await screen.findByTestId('tlp-field')).toHaveValue( + Object.values(TheHiveTLP).indexOf(TheHiveTLP.RED).toString() + ); }); }); diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx index 08ac059c1e5ecb..ed5eb8ae227eec 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { SelectField } from '@kbn/es-ui-shared-plugin/static/forms/components'; -import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { UseField, useFormContext } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import type { ConnectorFieldsProps } from '../types'; import * as i18n from './translations'; @@ -15,30 +15,20 @@ import { TheHiveTLP } from './types'; const { emptyField } = fieldValidators; -const tlpOptions: Array<{ text: string; value: string }> = [ - { - text: 'CLEAR', - value: TheHiveTLP.CLEAR, - }, - { - text: 'GREEN', - value: TheHiveTLP.GREEN, - }, - { - text: 'AMBER', - value: TheHiveTLP.AMBER, - }, - { - text: 'AMBER+STRICT', - value: TheHiveTLP.AMBER_STRICT, - }, - { - text: 'RED', - value: TheHiveTLP.RED, - }, -]; +const tlpOptions: Array<{ text: string; value: number }> = Object.entries(TheHiveTLP).map( + ([_, value], index) => ({ + text: value, + value: index, + }) +); const TheHiveFieldsComponent: React.FunctionComponent = () => { + const form = useFormContext(); + + const onTLPChange: (value: string) => void = (value: string) => { + form.setFieldValue('fields.tlp', parseInt(value, 10)); + }; + return (
= () validator: emptyField(i18n.TLP_REQUIRED), }, ], + defaultValue: tlpOptions[2].value, }} + onChange={onTLPChange} componentProps={{ euiFieldProps: { 'data-test-subj': 'tlp-field', options: tlpOptions, fullWidth: true, - hasNoInitialSelection: true, }, }} /> diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.test.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.test.tsx index 0c4e43f07efb59..37397195231ee3 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.test.tsx @@ -15,7 +15,7 @@ import { createQueryWithMarkup } from '../../../common/test_utils'; describe('TheHive Fields: Preview', () => { const fields = { - tlp: '1', + tlp: 1, }; let appMockRenderer: AppMockRenderer; diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.tsx index a4d8912e35c740..030bd29dc4bfbc 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.tsx +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.tsx @@ -12,22 +12,11 @@ import { ConnectorTypes } from '../../../../common/types/domain'; import type { ConnectorFieldsPreviewProps } from '../types'; import { ConnectorCard } from '../card'; import * as i18n from './translations'; +import { TheHiveTLP } from './types'; -const mapTLP = (tlpValue: number | string): string => { - switch (tlpValue) { - case '0': - return 'CLEAR'; - case '1': - return 'GREEN'; - case '2': - return 'AMBER'; - case '3': - return 'AMBER+STRICT'; - case '4': - return 'RED'; - default: - return 'AMBER'; - } +const mapTLP = (tlpValue: number): string => { + const tlpValues = Object.values(TheHiveTLP); + return tlpValues[tlpValue] || TheHiveTLP.AMBER; }; const TheHiveFieldsPreviewComponent: React.FunctionComponent< diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/types.ts b/x-pack/plugins/cases/public/components/connectors/thehive/types.ts index 78c4dce8b52ba8..12927fb643f84c 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/types.ts +++ b/x-pack/plugins/cases/public/components/connectors/thehive/types.ts @@ -6,9 +6,9 @@ */ export enum TheHiveTLP { - CLEAR = '0', - GREEN = '1', - AMBER = '2', - AMBER_STRICT = '3', - RED = '4', + CLEAR = 'CLEAR', + GREEN = 'GREEN', + AMBER = 'AMBER', + AMBER_STRICT = 'AMBER+STRICT', + RED = 'RED', } diff --git a/x-pack/plugins/cases/server/connectors/thehive/format.test.ts b/x-pack/plugins/cases/server/connectors/thehive/format.test.ts index eeced683cf393a..46f24aea7f4ba5 100644 --- a/x-pack/plugins/cases/server/connectors/thehive/format.test.ts +++ b/x-pack/plugins/cases/server/connectors/thehive/format.test.ts @@ -12,7 +12,7 @@ describe('TheHive formatter', () => { const theCase = { tags: ['tag1'], severity: 'high', - connector: { fields: { tlp: '1' } }, + connector: { fields: { tlp: 1 } }, } as Case; it('it formats correctly', async () => { diff --git a/x-pack/plugins/cases/server/connectors/thehive/format.ts b/x-pack/plugins/cases/server/connectors/thehive/format.ts index 8dd9fa7c191b9f..00bc4f197e7a93 100644 --- a/x-pack/plugins/cases/server/connectors/thehive/format.ts +++ b/x-pack/plugins/cases/server/connectors/thehive/format.ts @@ -27,7 +27,7 @@ export const format: Format = (theCase) => { const { tlp = null } = (theCase.connector.fields as ConnectorTheHiveTypeFields['fields']) ?? {}; return { tags: theCase.tags, - tlp: typeof tlp === 'string' ? parseInt(tlp, 10) : tlp, + tlp, severity: mapSeverity(theCase.severity), }; }; From 0b5da8a5fbfc6956c9f67ecb66c84e1706e542cf Mon Sep 17 00:00:00 2001 From: brijesh-elastic Date: Tue, 10 Sep 2024 13:03:55 +0530 Subject: [PATCH 8/8] Update TheHiveTLP enum to be safer in the future --- .../components/connectors/thehive/case_fields.test.tsx | 4 +--- .../components/connectors/thehive/case_fields.tsx | 10 ++++------ .../connectors/thehive/case_fields_preview.tsx | 4 ++-- .../public/components/connectors/thehive/types.ts | 10 +++++----- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx index 13653e3301107f..054e61ba843804 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.test.tsx @@ -45,8 +45,6 @@ describe('TheHive Cases Fields', () => { ); userEvent.selectOptions(screen.getByTestId('tlp-field'), '4'); - expect(await screen.findByTestId('tlp-field')).toHaveValue( - Object.values(TheHiveTLP).indexOf(TheHiveTLP.RED).toString() - ); + expect(await screen.findByTestId('tlp-field')).toHaveValue(TheHiveTLP.RED.toString()); }); }); diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx index ed5eb8ae227eec..554acbc5eb00b0 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields.tsx @@ -15,11 +15,9 @@ import { TheHiveTLP } from './types'; const { emptyField } = fieldValidators; -const tlpOptions: Array<{ text: string; value: number }> = Object.entries(TheHiveTLP).map( - ([_, value], index) => ({ - text: value, - value: index, - }) +const tlpOptions = Object.entries(TheHiveTLP).reduce>( + (acc, [key, value]) => (typeof value === 'number' ? [...acc, { text: key, value }] : acc), + [] ); const TheHiveFieldsComponent: React.FunctionComponent = () => { @@ -41,7 +39,7 @@ const TheHiveFieldsComponent: React.FunctionComponent = () validator: emptyField(i18n.TLP_REQUIRED), }, ], - defaultValue: tlpOptions[2].value, + defaultValue: TheHiveTLP.AMBER, }} onChange={onTLPChange} componentProps={{ diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.tsx b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.tsx index 030bd29dc4bfbc..cda520607807d7 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.tsx +++ b/x-pack/plugins/cases/public/components/connectors/thehive/case_fields_preview.tsx @@ -15,8 +15,8 @@ import * as i18n from './translations'; import { TheHiveTLP } from './types'; const mapTLP = (tlpValue: number): string => { - const tlpValues = Object.values(TheHiveTLP); - return tlpValues[tlpValue] || TheHiveTLP.AMBER; + const entry = Object.entries(TheHiveTLP).find(([_, value]) => value === tlpValue); + return entry?.[0] ?? 'AMBER'; }; const TheHiveFieldsPreviewComponent: React.FunctionComponent< diff --git a/x-pack/plugins/cases/public/components/connectors/thehive/types.ts b/x-pack/plugins/cases/public/components/connectors/thehive/types.ts index 12927fb643f84c..576eee60af8912 100644 --- a/x-pack/plugins/cases/public/components/connectors/thehive/types.ts +++ b/x-pack/plugins/cases/public/components/connectors/thehive/types.ts @@ -6,9 +6,9 @@ */ export enum TheHiveTLP { - CLEAR = 'CLEAR', - GREEN = 'GREEN', - AMBER = 'AMBER', - AMBER_STRICT = 'AMBER+STRICT', - RED = 'RED', + CLEAR = 0, + GREEN = 1, + AMBER = 2, + 'AMBER+STRICT' = 3, + RED = 4, }