diff --git a/docs/management/connectors/action-types/thehive.asciidoc b/docs/management/connectors/action-types/thehive.asciidoc
index e847d0b621eca..fdd6f975816ba 100644
--- a/docs/management/connectors/action-types/thehive.asciidoc
+++ b/docs/management/connectors/action-types/thehive.asciidoc
@@ -65,6 +65,7 @@ Additional comments:: Additional information about the incident.
Type:: The type of alert.
Source:: The source of the alert.
Source reference:: A source reference for the alert.
+Template:: Templates provide predefined configurations that include observables and procedures. When you select a template, its corresponding values will automatically populate the `Body` field. You can also use the `Build Your Own` option to create a custom template.
[float]
[[thehive-connector-networking-configuration]]
diff --git a/x-pack/platform/plugins/shared/stack_connectors/common/thehive/schema.ts b/x-pack/platform/plugins/shared/stack_connectors/common/thehive/schema.ts
index e880ca900591a..0e9ad5d81ad53 100644
--- a/x-pack/platform/plugins/shared/stack_connectors/common/thehive/schema.ts
+++ b/x-pack/platform/plugins/shared/stack_connectors/common/thehive/schema.ts
@@ -57,6 +57,8 @@ export const ExecutorSubActionCreateAlertParamsSchema = schema.object({
severity: schema.nullable(schema.number({ defaultValue: TheHiveSeverity.MEDIUM })),
tlp: schema.nullable(schema.number({ defaultValue: TheHiveTLP.AMBER })),
tags: schema.nullable(schema.arrayOf(schema.string())),
+ template: schema.number(),
+ body: schema.nullable(schema.string()),
});
export const ExecutorParamsSchema = schema.oneOf([
diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/constants.ts b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/constants.ts
index 6ab86cdbe7f1b..6c542b675efce 100644
--- a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/constants.ts
+++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/constants.ts
@@ -112,3 +112,117 @@ export const tlpOptions = [
}),
},
];
+
+export const templateOptions = [
+ {
+ value: 0,
+ text: i18n.translate(
+ 'xpack.stackConnectors.components.thehive.eventSelectTemplate1OptionLabel',
+ {
+ defaultMessage: 'Build Your Own',
+ }
+ ),
+ },
+ {
+ value: 1,
+ text: i18n.translate(
+ 'xpack.stackConnectors.components.thehive.eventSelectTemplate2OptionLabel',
+ {
+ defaultMessage: 'Compromised User Account Investigation',
+ }
+ ),
+ },
+ {
+ value: 2,
+ text: i18n.translate(
+ 'xpack.stackConnectors.components.thehive.eventSelectTemplate3OptionLabel',
+ {
+ defaultMessage: 'Malicious File Analysis',
+ }
+ ),
+ },
+ {
+ value: 3,
+ text: i18n.translate(
+ 'xpack.stackConnectors.components.thehive.eventSelectTemplate4OptionLabel',
+ {
+ defaultMessage: 'Suspicious Network Activity',
+ }
+ ),
+ },
+];
+
+export const bodyOptions = [
+ '{}',
+ '{\r\n "observables": [\r\n {\r\n "dataType": "mail",\r\n "data": "{{#context.alerts}}{{user.email}}{{/context.alerts}}",\r\n "tags": [\r\n "phishing",\r\n "targeted-user"\r\n ]\r\n },\r\n {\r\n "dataType": "other",\r\n "data": "{{#context.alerts}}{{user.name}}{{/context.alerts}}",\r\n "tags": [\r\n "username",\r\n "compromised-account",\r\n "unauthorized-access"\r\n ]\r\n }\r\n ],\r\n "procedures": [\r\n {\r\n "patternId": "{{#context.alerts}}{{threat.technique.id}}{{/context.alerts}}",\r\n "occurDate": {{#context.alerts}}{{#signal.original_time}}{{#FormatDate}} {{{signal.original_time}}} ; ; x {{/FormatDate}}{{/signal.original_time}}{{^signal.original_time}}1640000000000{{/signal.original_time}}{{/context.alerts}}\r\n }\r\n ]\r\n}',
+ '{\r\n "observables": [\r\n {\r\n "dataType": "hash",\r\n "data": "{{#context.alerts}}{{file.hash.md5}}{{/context.alerts}}",\r\n "tags": ["malware", "file-analysis"]\r\n },\r\n {\r\n "dataType": "hash",\r\n "data": "{{#context.alerts}}{{file.hash.sha256}}{{/context.alerts}}",\r\n "tags": ["malware", "suspicious-file"]\r\n }\r\n ]\r\n}',
+ '{\r\n "observables":\r\n [\r\n {\r\n "dataType": "ip",\r\n "data": "{{#context.alerts}}{{threat.indicator.ip}}{{/context.alerts}}",\r\n "tags": ["source", "malicious-activity"]\r\n }\r\n ],\r\n "procedures":\r\n [\r\n {\r\n "patternId": "{{#context.alerts}}{{threat.technique.id}}{{/context.alerts}}",\r\n "occurDate": {{#context.alerts}}{{#signal.original_time}}{{#FormatDate}} {{{signal.original_time}}} ; ; x {{/FormatDate}}{{/signal.original_time}}{{^signal.original_time}}1640000000000{{/signal.original_time}}{{/context.alerts}}\r\n }\r\n ]\r\n}\r\n',
+];
+
+export const testBodyOptions = [
+ '{}',
+ JSON.stringify(
+ {
+ observables: [
+ {
+ dataType: 'mail',
+ data: 'john@example.com',
+ tags: ['iam-user'],
+ },
+ {
+ dataType: 'other',
+ data: 'john',
+ tags: ['username'],
+ },
+ ],
+ procedures: [
+ {
+ patternId: 'T1132',
+ occurDate: 1737103254000,
+ },
+ ],
+ },
+ null,
+ 2
+ ),
+ JSON.stringify(
+ {
+ observables: [
+ {
+ dataType: 'hash',
+ data: '5d41402abc4b2a76b9719d911017c592',
+ tags: ['md5'],
+ },
+ ],
+ procedures: [
+ {
+ patternId: 'T1612',
+ occurDate: 1737107904000,
+ tactic: 'Defense Evasion',
+ },
+ ],
+ },
+ null,
+ 2
+ ),
+ JSON.stringify(
+ {
+ observables: [
+ {
+ dataType: 'ip',
+ data: '127.0.0.1',
+ tags: ['source'],
+ },
+ ],
+ procedures: [
+ {
+ patternId: 'T1132',
+ occurDate: 1737105104000,
+ tactic: 'command-and-control',
+ },
+ ],
+ },
+ null,
+ 2
+ ),
+];
diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params.test.tsx b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params.test.tsx
index d69080938fc26..7eeddac659020 100644
--- a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params.test.tsx
+++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params.test.tsx
@@ -72,6 +72,8 @@ describe('TheHiveParamsFields renders', () => {
severity: 2,
tags: [],
sourceRef: '{{alert.uuid}}',
+ template: 0,
+ body: '{}',
},
0
);
diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params.tsx b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params.tsx
index f0221ce7a460b..42900a9f3dacf 100644
--- a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params.tsx
+++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params.tsx
@@ -83,6 +83,8 @@ const TheHiveParamsFields: React.FunctionComponent
)}
>
diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params_alert.test.tsx b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params_alert.test.tsx
index 138595bd52690..4467a4d894dc8 100644
--- a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params_alert.test.tsx
+++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params_alert.test.tsx
@@ -6,11 +6,12 @@
*/
import React from 'react';
-import { render } from '@testing-library/react';
+import { fireEvent, render } from '@testing-library/react';
import { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types';
import { TheHiveParamsAlertFields } from './params_alert';
import { SUB_ACTION } from '../../../common/thehive/constants';
import { ExecutorParams, ExecutorSubActionCreateAlertParams } from '../../../common/thehive/types';
+import { bodyOptions } from './constants';
describe('TheHiveParamsFields renders', () => {
const subActionParams: ExecutorSubActionCreateAlertParams = {
@@ -22,6 +23,8 @@ describe('TheHiveParamsFields renders', () => {
source: 'source test',
type: 'sourceType test',
sourceRef: 'sourceRef test',
+ template: 0,
+ body: null,
};
const actionParams: ExecutorParams = {
subAction: SUB_ACTION.CREATE_ALERT,
@@ -63,8 +66,31 @@ describe('TheHiveParamsFields renders', () => {
expect(getByTestId('typeInput')).toBeInTheDocument();
expect(getByTestId('sourceInput')).toBeInTheDocument();
expect(getByTestId('sourceRefInput')).toBeInTheDocument();
+ expect(getByTestId('templateSelectInput')).toBeInTheDocument();
expect(getByTestId('severitySelectInput')).toHaveValue('2');
expect(getByTestId('tlpSelectInput')).toHaveValue('2');
+ expect(getByTestId('templateSelectInput')).toHaveValue('0');
+ });
+
+ it('changes the content of json editor when template is changed', () => {
+ const { getByTestId } = render();
+ const templateSelectEl = getByTestId('templateSelectInput');
+
+ fireEvent.change(templateSelectEl, { target: { value: 1 } });
+ expect(editAction).toHaveBeenNthCalledWith(
+ 1,
+ 'subActionParams',
+ { ...subActionParams, body: bodyOptions[1], template: 1 },
+ 0
+ );
+
+ fireEvent.change(templateSelectEl, { target: { value: 2 } });
+ expect(editAction).toHaveBeenNthCalledWith(
+ 2,
+ 'subActionParams',
+ { ...subActionParams, body: bodyOptions[2], template: 2 },
+ 0
+ );
});
});
diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params_alert.tsx b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params_alert.tsx
index 868298ef98a7c..c2d7a6dde0925 100644
--- a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params_alert.tsx
+++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/params_alert.tsx
@@ -10,10 +10,18 @@ import {
TextFieldWithMessageVariables,
TextAreaWithMessageVariables,
ActionParamsProps,
+ JsonEditorWithMessageVariables,
+ ActionConnectorMode,
} from '@kbn/triggers-actions-ui-plugin/public';
-import { EuiFormRow, EuiSelect, EuiComboBox } from '@elastic/eui';
+import { EuiFormRow, EuiSelect, EuiComboBox, EuiIconTip } from '@elastic/eui';
import { ExecutorParams, ExecutorSubActionCreateAlertParams } from '../../../common/thehive/types';
-import { severityOptions, tlpOptions } from './constants';
+import {
+ bodyOptions,
+ severityOptions,
+ templateOptions,
+ testBodyOptions,
+ tlpOptions,
+} from './constants';
import * as translations from './translations';
export const TheHiveParamsAlertFields: React.FC> = ({
@@ -22,6 +30,7 @@ export const TheHiveParamsAlertFields: React.FC {
const alert = useMemo(
() =>
@@ -30,9 +39,12 @@ export const TheHiveParamsAlertFields: React.FC executionMode === ActionConnectorMode.Test, [executionMode]);
const [severity, setSeverity] = useState(alert.severity ?? severityOptions[1].value);
const [tlp, setTlp] = useState(alert.tlp ?? tlpOptions[2].value);
@@ -187,6 +199,57 @@ export const TheHiveParamsAlertFields: React.FC
+
+ {
+ editAction(
+ 'subActionParams',
+ {
+ ...alert,
+ body: isTest
+ ? testBodyOptions[parseInt(e.target.value, 10)]
+ : bodyOptions[parseInt(e.target.value, 10)],
+ template: parseInt(e.target.value, 10),
+ },
+ index
+ );
+ }}
+ />
+
+
+ {translations.BODY_LABEL}
+
+ >
+ }
+ ariaLabel={translations.BODY_DESCRIPTION}
+ errors={errors.body as string[]}
+ onDocumentsChange={(json: string) =>
+ editAction('subActionParams', { ...alert, body: json }, index)
+ }
+ dataTestSubj="thehive-body"
+ onBlur={() => {
+ if (!alert.body) {
+ editAction('subActionParams', { ...alert, body: '{}' }, index);
+ }
+ }}
+ />
>
);
};
diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/thehive.test.tsx b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/thehive.test.tsx
index 654324d22e153..7923da52afbd7 100644
--- a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/thehive.test.tsx
+++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/thehive.test.tsx
@@ -92,6 +92,19 @@ describe('thehive createAlert action params validation', () => {
type: 'type test',
source: 'source test',
sourceRef: 'source reference test',
+ body: JSON.stringify(
+ {
+ observables: [
+ {
+ dataType: 'ip',
+ data: '127.0.0.1',
+ tags: ['source.ip'],
+ },
+ ],
+ },
+ null,
+ 2
+ ),
},
comments: [],
};
diff --git a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/translations.ts b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/translations.ts
index cd2c1ffcf9a63..67fdac461835a 100644
--- a/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/translations.ts
+++ b/x-pack/platform/plugins/shared/stack_connectors/public/connector_types/thehive/translations.ts
@@ -102,6 +102,42 @@ export const SOURCE_REF_LABEL = i18n.translate(
}
);
+export const TEMPLATE_LABEL = i18n.translate(
+ 'xpack.stackConnectors.components.thehive.templateFieldLabel',
+ {
+ defaultMessage: 'Template',
+ }
+);
+
+export const BODY_LABEL = i18n.translate(
+ 'xpack.stackConnectors.components.thehive.bodyFieldLabel',
+ {
+ defaultMessage: 'Body',
+ }
+);
+
+export const BODY_DESCRIPTION = i18n.translate(
+ 'xpack.stackConnectors.components.thehive.bodyFieldLabel',
+ {
+ defaultMessage: 'Code Editor',
+ }
+);
+
+export const BODY_HELP_LABEL = i18n.translate(
+ 'xpack.stackConnectors.components.thehive.bodyFieldHelpText',
+ {
+ defaultMessage: 'Body Help',
+ }
+);
+
+export const BODY_HELP_TEXT = i18n.translate(
+ 'xpack.stackConnectors.components.thehive.bodyFieldHelpText',
+ {
+ defaultMessage:
+ 'Additional body parameters such as observables, procedures (TTPs), and custom fields to include in the API request.',
+ }
+);
+
export const TITLE_REQUIRED = i18n.translate(
'xpack.stackConnectors.components.thehive.requiredTitleText',
{
diff --git a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/thehive/thehive.test.ts b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/thehive/thehive.test.ts
index 5972d5da570ef..fdeb0ca07d0c0 100644
--- a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/thehive/thehive.test.ts
+++ b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/thehive/thehive.test.ts
@@ -390,7 +390,7 @@ describe('TheHiveConnector', () => {
papLabel: 'AMBER',
follow: true,
customFields: [],
- observableCount: 0,
+ observableCount: 1,
status: 'New',
stage: 'New',
extraData: {},
@@ -415,8 +415,32 @@ describe('TheHiveConnector', () => {
severity: 1,
tlp: 2,
tags: ['tag1', 'tag2'],
+ template: 0,
+ body: JSON.stringify(
+ {
+ observables: [
+ {
+ dataType: 'url',
+ data: 'http://example.com',
+ tags: ['url'],
+ },
+ ],
+ procedures: [
+ {
+ patternId: 'T1132',
+ occurDate: 1640000000000,
+ tactic: 'command-and-control',
+ },
+ ],
+ },
+ null,
+ 2
+ ),
};
+ const { body, template, ...restOfAlert } = alert;
+ const expectedAlertBody = { ...restOfAlert, ...JSON.parse(body ?? '{}') };
+
it('TheHive API call is successful with correct parameters', async () => {
await connector.createAlert(alert, connectorUsageCollector);
expect(mockRequest).toBeCalledTimes(1);
@@ -425,7 +449,7 @@ describe('TheHiveConnector', () => {
url: 'https://example.com/api/v1/alert',
method: 'post',
responseSchema: TheHiveCreateAlertResponseSchema,
- data: alert,
+ data: expectedAlertBody,
headers: {
Authorization: 'Bearer test123',
'X-Organisation': null,
diff --git a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/thehive/thehive.ts b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/thehive/thehive.ts
index 623a9b8ee73d7..d291308c858af 100644
--- a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/thehive/thehive.ts
+++ b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/thehive/thehive.ts
@@ -157,11 +157,15 @@ export class TheHiveConnector extends CaseConnector<
alert: ExecutorSubActionCreateAlertParams,
connectorUsageCollector: ConnectorUsageCollector
) {
+ const { body, template, ...restOfAlert } = alert;
+ const bodyJson = JSON.parse(body ?? '{}');
+ const mergedAlertBody = { ...restOfAlert, ...bodyJson };
+
await this.request(
{
method: 'post',
url: `${this.url}/api/${API_VERSION}/alert`,
- data: alert,
+ data: mergedAlertBody,
headers: this.getAuthHeaders(),
responseSchema: TheHiveCreateAlertResponseSchema,
},
diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx
index dd761d42037b2..aa19bc264e7fe 100644
--- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx
+++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/components/json_editor_with_message_variables.tsx
@@ -77,7 +77,7 @@ export const JsonEditorWithMessageVariables: React.FunctionComponent = ({
const { convertToJson, setXJson, xJson } = useXJsonMode(inputTargetValue ?? null);
useEffect(() => {
- if (!xJson && inputTargetValue) {
+ if ((!xJson && inputTargetValue) || (xJson && inputTargetValue && inputTargetValue !== xJson)) {
setXJson(inputTargetValue);
}
// eslint-disable-next-line react-hooks/exhaustive-deps