Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/management/connectors/action-types/thehive.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: '[email protected]',
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
),
];
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ describe('TheHiveParamsFields renders', () => {
severity: 2,
tags: [],
sourceRef: '{{alert.uuid}}',
template: 0,
body: '{}',
},
0
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ const TheHiveParamsFields: React.FunctionComponent<ActionParamsProps<ExecutorPar
severity: 2,
tags: [],
sourceRef: isTest ? undefined : '{{alert.uuid}}',
template: 0,
body: '{}',
}
: {
incident: {
Expand Down Expand Up @@ -123,6 +125,7 @@ const TheHiveParamsFields: React.FunctionComponent<ActionParamsProps<ExecutorPar
index={index}
errors={errors}
messageVariables={messageVariables}
executionMode={executionMode}
/>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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,
Expand Down Expand Up @@ -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(<TheHiveParamsAlertFields {...defaultProps} />);
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
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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<ActionParamsProps<ExecutorParams>> = ({
Expand All @@ -22,6 +30,7 @@ export const TheHiveParamsAlertFields: React.FC<ActionParamsProps<ExecutorParams
index,
errors,
messageVariables,
executionMode,
}) => {
const alert = useMemo(
() =>
Expand All @@ -30,9 +39,12 @@ export const TheHiveParamsAlertFields: React.FC<ActionParamsProps<ExecutorParams
tlp: 2,
severity: 2,
tags: [],
template: 0,
body: '{}',
} as unknown as ExecutorSubActionCreateAlertParams),
[actionParams.subActionParams]
);
const isTest = useMemo(() => executionMode === ActionConnectorMode.Test, [executionMode]);

const [severity, setSeverity] = useState(alert.severity ?? severityOptions[1].value);
const [tlp, setTlp] = useState(alert.tlp ?? tlpOptions[2].value);
Expand Down Expand Up @@ -187,6 +199,57 @@ export const TheHiveParamsAlertFields: React.FC<ActionParamsProps<ExecutorParams
noSuggestions
/>
</EuiFormRow>
<EuiFormRow fullWidth label={translations.TEMPLATE_LABEL}>
<EuiSelect
fullWidth
data-test-subj="templateSelectInput"
value={alert.template}
options={templateOptions}
onChange={(e) => {
editAction(
'subActionParams',
{
...alert,
body: isTest
? testBodyOptions[parseInt(e.target.value, 10)]
: bodyOptions[parseInt(e.target.value, 10)],
template: parseInt(e.target.value, 10),
},
index
);
}}
/>
</EuiFormRow>
<JsonEditorWithMessageVariables
messageVariables={messageVariables}
paramsProperty={'body'}
inputTargetValue={alert.body}
label={
<>
{translations.BODY_LABEL}
<EuiIconTip
size="s"
color="subdued"
type="questionInCircle"
className="eui-alignTop"
data-test-subj="otherFieldsHelpTooltip"
aria-label={translations.BODY_HELP_LABEL}
content={translations.BODY_HELP_TEXT}
/>
</>
}
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);
}
}}
/>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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: [],
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
{
Expand Down
Loading