mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[RAC][Security Solution] Add flattened parameters object and populate it in Security Solution (#120698) (#121205)
* Add flattend parameters object and populate it in Security Solution * Fix severity, risk_score, bugs, tests * Add ALERT_RULE_PARAMETERS to package * Skip tightly coupled test * fix more tests * Remove unused import * Fix threat matching API test * Continue overriding kibana.alert.rule.risk_score and severity for now * Add ignore_above to ALERT_RULE_PARAMETERS Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Marshall Main <55718608+marshallmain@users.noreply.github.com>
This commit is contained in:
parent
90e290d222
commit
04ec4410a5
13 changed files with 396 additions and 92 deletions
|
@ -54,6 +54,7 @@ const ALERT_RULE_CATEGORY = `${ALERT_RULE_NAMESPACE}.category` as const;
|
|||
const ALERT_RULE_NAME = `${ALERT_RULE_NAMESPACE}.name` as const;
|
||||
const ALERT_RULE_NOTE = `${ALERT_RULE_NAMESPACE}.note` as const;
|
||||
const ALERT_RULE_PARAMS = `${ALERT_RULE_NAMESPACE}.params` as const;
|
||||
const ALERT_RULE_PARAMETERS = `${ALERT_RULE_NAMESPACE}.parameters` as const;
|
||||
const ALERT_RULE_REFERENCES = `${ALERT_RULE_NAMESPACE}.references` as const;
|
||||
const ALERT_RULE_RISK_SCORE = `${ALERT_RULE_NAMESPACE}.risk_score` as const;
|
||||
const ALERT_RULE_RISK_SCORE_MAPPING = `${ALERT_RULE_NAMESPACE}.risk_score_mapping` as const;
|
||||
|
@ -113,6 +114,7 @@ const fields = {
|
|||
ALERT_RULE_NAME,
|
||||
ALERT_RULE_NOTE,
|
||||
ALERT_RULE_PARAMS,
|
||||
ALERT_RULE_PARAMETERS,
|
||||
ALERT_RULE_REFERENCES,
|
||||
ALERT_RULE_RISK_SCORE,
|
||||
ALERT_RULE_RISK_SCORE_MAPPING,
|
||||
|
@ -170,6 +172,7 @@ export {
|
|||
ALERT_RULE_NAME,
|
||||
ALERT_RULE_NOTE,
|
||||
ALERT_RULE_PARAMS,
|
||||
ALERT_RULE_PARAMETERS,
|
||||
ALERT_RULE_REFERENCES,
|
||||
ALERT_RULE_RISK_SCORE,
|
||||
ALERT_RULE_RISK_SCORE_MAPPING,
|
||||
|
|
|
@ -17,6 +17,7 @@ export const technicalRuleFieldMap = {
|
|||
Fields.TAGS
|
||||
),
|
||||
[Fields.ALERT_RULE_PARAMS]: { type: 'keyword', index: false },
|
||||
[Fields.ALERT_RULE_PARAMETERS]: { type: 'flattened', ignore_above: 4096 },
|
||||
[Fields.ALERT_RULE_TYPE_ID]: { type: 'keyword', required: true },
|
||||
[Fields.ALERT_RULE_CONSUMER]: { type: 'keyword', required: true },
|
||||
[Fields.ALERT_RULE_PRODUCER]: { type: 'keyword', required: true },
|
||||
|
|
|
@ -55,7 +55,7 @@ describe('Alert details with unmapped fields', () => {
|
|||
});
|
||||
|
||||
// This test needs to be updated to not look for the field in a specific row, as it prevents us from adding/removing fields
|
||||
it('Displays the unmapped field on the table', () => {
|
||||
it.skip('Displays the unmapped field on the table', () => {
|
||||
const expectedUnmmappedField = {
|
||||
row: 82,
|
||||
field: 'unmapped',
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
ALERT_RULE_UUID,
|
||||
ALERT_RULE_NAME,
|
||||
ALERT_INSTANCE_ID,
|
||||
ALERT_RULE_PARAMETERS,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import { flattenWithPrefix } from '@kbn/securitysolution-rules';
|
||||
|
||||
|
@ -33,7 +34,6 @@ import {
|
|||
ALERT_ORIGINAL_TIME,
|
||||
ALERT_ORIGINAL_EVENT,
|
||||
} from '../../../../../common/field_maps/field_names';
|
||||
import { WrappedRACAlert } from '../types';
|
||||
|
||||
export const mockThresholdResults = {
|
||||
rawResponse: {
|
||||
|
@ -77,7 +77,7 @@ export const mockThresholdResults = {
|
|||
},
|
||||
};
|
||||
|
||||
export const sampleThresholdAlert: WrappedRACAlert = {
|
||||
export const sampleThresholdAlert = {
|
||||
_id: 'b3ad77a4-65bd-4c4e-89cf-13c46f54bc4d',
|
||||
_index: 'some-index',
|
||||
_source: {
|
||||
|
@ -111,6 +111,30 @@ export const sampleThresholdAlert: WrappedRACAlert = {
|
|||
[ALERT_RULE_PRODUCER]: 'siem',
|
||||
[ALERT_RULE_TYPE_ID]: 'query-rule-id',
|
||||
[ALERT_RULE_UUID]: '151af49f-2e82-4b6f-831b-7f8cb341a5ff',
|
||||
[ALERT_RULE_PARAMETERS]: {
|
||||
author: [],
|
||||
description: 'some description',
|
||||
false_positives: ['false positive 1', 'false positive 2'],
|
||||
from: 'now-6m',
|
||||
immutable: false,
|
||||
query: 'user.name: root or user.name: admin',
|
||||
references: ['test 1', 'test 2'],
|
||||
severity: 'high',
|
||||
severity_mapping: [],
|
||||
to: 'now',
|
||||
type: 'query',
|
||||
threat: [],
|
||||
threshold: {
|
||||
field: ['source.ip', 'host.name'],
|
||||
value: 1,
|
||||
},
|
||||
max_signals: 100,
|
||||
risk_score: 55,
|
||||
risk_score_mapping: [],
|
||||
language: 'kuery',
|
||||
rule_id: 'f88a544c-1d4e-4652-ae2a-c953b38da5d0',
|
||||
exceptions_list: getListArrayMock(),
|
||||
},
|
||||
...(flattenWithPrefix(ALERT_RULE_NAMESPACE, {
|
||||
author: [],
|
||||
uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9f9',
|
||||
|
@ -132,10 +156,6 @@ export const sampleThresholdAlert: WrappedRACAlert = {
|
|||
to: 'now',
|
||||
type: 'query',
|
||||
threat: [],
|
||||
threshold: {
|
||||
field: ['source.ip', 'host.name'],
|
||||
value: 1,
|
||||
},
|
||||
version: 1,
|
||||
status: 'succeeded',
|
||||
status_date: '2020-02-22T16:47:50.047Z',
|
||||
|
|
|
@ -8,9 +8,12 @@
|
|||
import {
|
||||
ALERT_INSTANCE_ID,
|
||||
ALERT_REASON,
|
||||
ALERT_RISK_SCORE,
|
||||
ALERT_RULE_CONSUMER,
|
||||
ALERT_RULE_NAMESPACE,
|
||||
ALERT_RULE_PARAMETERS,
|
||||
ALERT_RULE_UUID,
|
||||
ALERT_SEVERITY,
|
||||
ALERT_STATUS,
|
||||
ALERT_STATUS_ACTIVE,
|
||||
ALERT_UUID,
|
||||
|
@ -26,10 +29,6 @@ import { flattenWithPrefix } from '@kbn/securitysolution-rules';
|
|||
import { sampleDocNoSortIdWithTimestamp } from '../../../signals/__mocks__/es_results';
|
||||
import { buildAlert, buildParent, buildAncestors, additionalAlertFields } from './build_alert';
|
||||
import { Ancestor, SignalSourceHit } from '../../../signals/types';
|
||||
import {
|
||||
getRulesSchemaMock,
|
||||
ANCHOR_DATE,
|
||||
} from '../../../../../../common/detection_engine/schemas/response/rules_schema.mocks';
|
||||
import { getListArrayMock } from '../../../../../../common/detection_engine/schemas/types/lists.mock';
|
||||
import { SERVER_APP_ID } from '../../../../../../common/constants';
|
||||
import { EVENT_DATASET } from '../../../../../../common/cti/constants';
|
||||
|
@ -38,7 +37,9 @@ import {
|
|||
ALERT_ORIGINAL_TIME,
|
||||
ALERT_DEPTH,
|
||||
ALERT_ORIGINAL_EVENT,
|
||||
ALERT_BUILDING_BLOCK_TYPE,
|
||||
} from '../../../../../../common/field_maps/field_names';
|
||||
import { getCompleteRuleMock, getQueryRuleParams } from '../../../schemas/rule_schemas.mock';
|
||||
|
||||
type SignalDoc = SignalSourceHit & {
|
||||
_source: Required<SignalSourceHit>['_source'] & { [TIMESTAMP]: string };
|
||||
|
@ -54,10 +55,10 @@ describe('buildAlert', () => {
|
|||
test('it builds an alert as expected without original_event if event does not exist', () => {
|
||||
const doc = sampleDocNoSortIdWithTimestamp('d5e8eb51-a6a0-456d-8a15-4b79bfec3d71');
|
||||
delete doc._source.event;
|
||||
const rule = getRulesSchemaMock();
|
||||
const completeRule = getCompleteRuleMock(getQueryRuleParams());
|
||||
const reason = 'alert reasonable reason';
|
||||
const alert = {
|
||||
...buildAlert([doc], rule, SPACE_ID, reason),
|
||||
...buildAlert([doc], completeRule, SPACE_ID, reason),
|
||||
...additionalAlertFields(doc),
|
||||
};
|
||||
const timestamp = alert[TIMESTAMP];
|
||||
|
@ -77,39 +78,134 @@ describe('buildAlert', () => {
|
|||
[ALERT_REASON]: 'alert reasonable reason',
|
||||
[ALERT_STATUS]: ALERT_STATUS_ACTIVE,
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_BUILDING_BLOCK_TYPE]: 'default',
|
||||
[ALERT_SEVERITY]: 'high',
|
||||
[ALERT_RISK_SCORE]: 50,
|
||||
[ALERT_RULE_PARAMETERS]: {
|
||||
description: 'Detecting root and admin users',
|
||||
risk_score: 50,
|
||||
severity: 'high',
|
||||
building_block_type: 'default',
|
||||
note: '# Investigative notes',
|
||||
license: 'Elastic License',
|
||||
timeline_id: 'some-timeline-id',
|
||||
timeline_title: 'some-timeline-title',
|
||||
meta: { someMeta: 'someField' },
|
||||
author: ['Elastic'],
|
||||
false_positives: [],
|
||||
from: 'now-6m',
|
||||
rule_id: 'rule-1',
|
||||
max_signals: 10000,
|
||||
risk_score_mapping: [],
|
||||
severity_mapping: [],
|
||||
threat: [
|
||||
{
|
||||
framework: 'MITRE ATT&CK',
|
||||
tactic: {
|
||||
id: 'TA0000',
|
||||
name: 'test tactic',
|
||||
reference: 'https://attack.mitre.org/tactics/TA0000/',
|
||||
},
|
||||
technique: [
|
||||
{
|
||||
id: 'T0000',
|
||||
name: 'test technique',
|
||||
reference: 'https://attack.mitre.org/techniques/T0000/',
|
||||
subtechnique: [
|
||||
{
|
||||
id: 'T0000.000',
|
||||
name: 'test subtechnique',
|
||||
reference: 'https://attack.mitre.org/techniques/T0000/000/',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
to: 'now',
|
||||
references: ['http://example.com', 'https://example.com'],
|
||||
version: 1,
|
||||
exceptions_list: [
|
||||
{
|
||||
id: 'some_uuid',
|
||||
list_id: 'list_id_single',
|
||||
namespace_type: 'single',
|
||||
type: 'detection',
|
||||
},
|
||||
{
|
||||
id: 'endpoint_list',
|
||||
list_id: 'endpoint_list',
|
||||
namespace_type: 'agnostic',
|
||||
type: 'endpoint',
|
||||
},
|
||||
],
|
||||
immutable: false,
|
||||
type: 'query',
|
||||
language: 'kuery',
|
||||
index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],
|
||||
query: 'user.name: root or user.name: admin',
|
||||
filters: [{ query: { match_phrase: { 'host.name': 'some-host' } } }],
|
||||
},
|
||||
...flattenWithPrefix(ALERT_RULE_NAMESPACE, {
|
||||
author: [],
|
||||
uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9f9',
|
||||
created_at: new Date(ANCHOR_DATE).toISOString(),
|
||||
updated_at: new Date(ANCHOR_DATE).toISOString(),
|
||||
created_by: 'elastic',
|
||||
description: 'some description',
|
||||
actions: [],
|
||||
author: ['Elastic'],
|
||||
uuid: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
|
||||
building_block_type: 'default',
|
||||
created_at: '2020-03-27T22:55:59.577Z',
|
||||
updated_at: '2020-03-27T22:55:59.577Z',
|
||||
created_by: 'sample user',
|
||||
description: 'Detecting root and admin users',
|
||||
enabled: true,
|
||||
false_positives: ['false positive 1', 'false positive 2'],
|
||||
false_positives: [],
|
||||
from: 'now-6m',
|
||||
immutable: false,
|
||||
name: 'Query with a rule id',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
references: ['test 1', 'test 2'],
|
||||
license: 'Elastic License',
|
||||
meta: {
|
||||
someMeta: 'someField',
|
||||
},
|
||||
name: 'rule-name',
|
||||
note: '# Investigative notes',
|
||||
references: ['http://example.com', 'https://example.com'],
|
||||
severity: 'high',
|
||||
severity_mapping: [],
|
||||
updated_by: 'elastic_kibana',
|
||||
updated_by: 'sample user',
|
||||
tags: ['some fake tag 1', 'some fake tag 2'],
|
||||
to: 'now',
|
||||
type: 'query',
|
||||
threat: [],
|
||||
threat: [
|
||||
{
|
||||
framework: 'MITRE ATT&CK',
|
||||
tactic: {
|
||||
id: 'TA0000',
|
||||
name: 'test tactic',
|
||||
reference: 'https://attack.mitre.org/tactics/TA0000/',
|
||||
},
|
||||
technique: [
|
||||
{
|
||||
id: 'T0000',
|
||||
name: 'test technique',
|
||||
reference: 'https://attack.mitre.org/techniques/T0000/',
|
||||
subtechnique: [
|
||||
{
|
||||
id: 'T0000.000',
|
||||
name: 'test subtechnique',
|
||||
reference: 'https://attack.mitre.org/techniques/T0000/000/',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
version: 1,
|
||||
status: 'succeeded',
|
||||
status_date: '2020-02-22T16:47:50.047Z',
|
||||
last_success_at: '2020-02-22T16:47:50.047Z',
|
||||
last_success_message: 'succeeded',
|
||||
max_signals: 100,
|
||||
risk_score: 55,
|
||||
max_signals: 10000,
|
||||
risk_score: 50,
|
||||
risk_score_mapping: [],
|
||||
language: 'kuery',
|
||||
rule_id: 'query-rule-id',
|
||||
rule_id: 'rule-1',
|
||||
interval: '5m',
|
||||
exceptions_list: getListArrayMock(),
|
||||
throttle: 'no_actions',
|
||||
timeline_id: 'some-timeline-id',
|
||||
timeline_title: 'some-timeline-title',
|
||||
}),
|
||||
[ALERT_DEPTH]: 1,
|
||||
};
|
||||
|
@ -128,13 +224,14 @@ describe('buildAlert', () => {
|
|||
[EVENT_MODULE]: 'system',
|
||||
},
|
||||
};
|
||||
const rule = getRulesSchemaMock();
|
||||
const completeRule = getCompleteRuleMock(getQueryRuleParams());
|
||||
const reason = 'alert reasonable reason';
|
||||
const alert = {
|
||||
...buildAlert([doc], rule, SPACE_ID, reason),
|
||||
...buildAlert([doc], completeRule, SPACE_ID, reason),
|
||||
...additionalAlertFields(doc),
|
||||
};
|
||||
const timestamp = alert[TIMESTAMP];
|
||||
|
||||
const expected = {
|
||||
[TIMESTAMP]: timestamp,
|
||||
[SPACE_IDS]: [SPACE_ID],
|
||||
|
@ -157,39 +254,134 @@ describe('buildAlert', () => {
|
|||
[ALERT_REASON]: 'alert reasonable reason',
|
||||
[ALERT_STATUS]: ALERT_STATUS_ACTIVE,
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_BUILDING_BLOCK_TYPE]: 'default',
|
||||
[ALERT_SEVERITY]: 'high',
|
||||
[ALERT_RISK_SCORE]: 50,
|
||||
[ALERT_RULE_PARAMETERS]: {
|
||||
description: 'Detecting root and admin users',
|
||||
risk_score: 50,
|
||||
severity: 'high',
|
||||
building_block_type: 'default',
|
||||
note: '# Investigative notes',
|
||||
license: 'Elastic License',
|
||||
timeline_id: 'some-timeline-id',
|
||||
timeline_title: 'some-timeline-title',
|
||||
meta: { someMeta: 'someField' },
|
||||
author: ['Elastic'],
|
||||
false_positives: [],
|
||||
from: 'now-6m',
|
||||
rule_id: 'rule-1',
|
||||
max_signals: 10000,
|
||||
risk_score_mapping: [],
|
||||
severity_mapping: [],
|
||||
threat: [
|
||||
{
|
||||
framework: 'MITRE ATT&CK',
|
||||
tactic: {
|
||||
id: 'TA0000',
|
||||
name: 'test tactic',
|
||||
reference: 'https://attack.mitre.org/tactics/TA0000/',
|
||||
},
|
||||
technique: [
|
||||
{
|
||||
id: 'T0000',
|
||||
name: 'test technique',
|
||||
reference: 'https://attack.mitre.org/techniques/T0000/',
|
||||
subtechnique: [
|
||||
{
|
||||
id: 'T0000.000',
|
||||
name: 'test subtechnique',
|
||||
reference: 'https://attack.mitre.org/techniques/T0000/000/',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
to: 'now',
|
||||
references: ['http://example.com', 'https://example.com'],
|
||||
version: 1,
|
||||
exceptions_list: [
|
||||
{
|
||||
id: 'some_uuid',
|
||||
list_id: 'list_id_single',
|
||||
namespace_type: 'single',
|
||||
type: 'detection',
|
||||
},
|
||||
{
|
||||
id: 'endpoint_list',
|
||||
list_id: 'endpoint_list',
|
||||
namespace_type: 'agnostic',
|
||||
type: 'endpoint',
|
||||
},
|
||||
],
|
||||
immutable: false,
|
||||
type: 'query',
|
||||
language: 'kuery',
|
||||
index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],
|
||||
query: 'user.name: root or user.name: admin',
|
||||
filters: [{ query: { match_phrase: { 'host.name': 'some-host' } } }],
|
||||
},
|
||||
...flattenWithPrefix(ALERT_RULE_NAMESPACE, {
|
||||
author: [],
|
||||
uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9f9',
|
||||
created_at: new Date(ANCHOR_DATE).toISOString(),
|
||||
updated_at: new Date(ANCHOR_DATE).toISOString(),
|
||||
created_by: 'elastic',
|
||||
description: 'some description',
|
||||
actions: [],
|
||||
author: ['Elastic'],
|
||||
uuid: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
|
||||
building_block_type: 'default',
|
||||
created_at: '2020-03-27T22:55:59.577Z',
|
||||
updated_at: '2020-03-27T22:55:59.577Z',
|
||||
created_by: 'sample user',
|
||||
description: 'Detecting root and admin users',
|
||||
enabled: true,
|
||||
false_positives: ['false positive 1', 'false positive 2'],
|
||||
false_positives: [],
|
||||
from: 'now-6m',
|
||||
immutable: false,
|
||||
name: 'Query with a rule id',
|
||||
query: 'user.name: root or user.name: admin',
|
||||
references: ['test 1', 'test 2'],
|
||||
license: 'Elastic License',
|
||||
meta: {
|
||||
someMeta: 'someField',
|
||||
},
|
||||
name: 'rule-name',
|
||||
note: '# Investigative notes',
|
||||
references: ['http://example.com', 'https://example.com'],
|
||||
severity: 'high',
|
||||
severity_mapping: [],
|
||||
updated_by: 'elastic_kibana',
|
||||
updated_by: 'sample user',
|
||||
tags: ['some fake tag 1', 'some fake tag 2'],
|
||||
to: 'now',
|
||||
type: 'query',
|
||||
threat: [],
|
||||
threat: [
|
||||
{
|
||||
framework: 'MITRE ATT&CK',
|
||||
tactic: {
|
||||
id: 'TA0000',
|
||||
name: 'test tactic',
|
||||
reference: 'https://attack.mitre.org/tactics/TA0000/',
|
||||
},
|
||||
technique: [
|
||||
{
|
||||
id: 'T0000',
|
||||
name: 'test technique',
|
||||
reference: 'https://attack.mitre.org/techniques/T0000/',
|
||||
subtechnique: [
|
||||
{
|
||||
id: 'T0000.000',
|
||||
name: 'test subtechnique',
|
||||
reference: 'https://attack.mitre.org/techniques/T0000/000/',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
version: 1,
|
||||
status: 'succeeded',
|
||||
status_date: '2020-02-22T16:47:50.047Z',
|
||||
last_success_at: '2020-02-22T16:47:50.047Z',
|
||||
last_success_message: 'succeeded',
|
||||
max_signals: 100,
|
||||
risk_score: 55,
|
||||
max_signals: 10000,
|
||||
risk_score: 50,
|
||||
risk_score_mapping: [],
|
||||
language: 'kuery',
|
||||
rule_id: 'query-rule-id',
|
||||
rule_id: 'rule-1',
|
||||
interval: '5m',
|
||||
exceptions_list: getListArrayMock(),
|
||||
throttle: 'no_actions',
|
||||
timeline_id: 'some-timeline-id',
|
||||
timeline_title: 'some-timeline-title',
|
||||
}),
|
||||
[ALERT_DEPTH]: 1,
|
||||
};
|
||||
|
|
|
@ -7,9 +7,12 @@
|
|||
|
||||
import {
|
||||
ALERT_REASON,
|
||||
ALERT_RISK_SCORE,
|
||||
ALERT_RULE_CONSUMER,
|
||||
ALERT_RULE_NAMESPACE,
|
||||
ALERT_RULE_PARAMETERS,
|
||||
ALERT_RULE_UUID,
|
||||
ALERT_SEVERITY,
|
||||
ALERT_STATUS,
|
||||
ALERT_STATUS_ACTIVE,
|
||||
ALERT_WORKFLOW_STATUS,
|
||||
|
@ -20,7 +23,6 @@ import { flattenWithPrefix } from '@kbn/securitysolution-rules';
|
|||
|
||||
import { createHash } from 'crypto';
|
||||
|
||||
import { RulesSchema } from '../../../../../../common/detection_engine/schemas/response/rules_schema';
|
||||
import { Ancestor, BaseSignalHit, SimpleHit, ThresholdResult } from '../../../signals/types';
|
||||
import {
|
||||
getField,
|
||||
|
@ -37,7 +39,15 @@ import {
|
|||
ALERT_ORIGINAL_TIME,
|
||||
ALERT_THRESHOLD_RESULT,
|
||||
ALERT_ORIGINAL_EVENT,
|
||||
ALERT_BUILDING_BLOCK_TYPE,
|
||||
} from '../../../../../../common/field_maps/field_names';
|
||||
import { CompleteRule, RuleParams } from '../../../schemas/rule_schemas';
|
||||
import {
|
||||
commonParamsCamelToSnake,
|
||||
typeSpecificCamelToSnake,
|
||||
} from '../../../schemas/rule_converters';
|
||||
import { transformTags } from '../../../routes/rules/utils';
|
||||
import { transformAlertToRuleAction } from '../../../../../../common/detection_engine/transform_actions';
|
||||
|
||||
export const generateAlertId = (alert: RACAlert) => {
|
||||
return createHash('sha256')
|
||||
|
@ -86,16 +96,40 @@ export const buildAncestors = (doc: SimpleHit): Ancestor[] => {
|
|||
*/
|
||||
export const buildAlert = (
|
||||
docs: SimpleHit[],
|
||||
rule: RulesSchema,
|
||||
completeRule: CompleteRule<RuleParams>,
|
||||
spaceId: string | null | undefined,
|
||||
reason: string
|
||||
reason: string,
|
||||
overrides?: {
|
||||
nameOverride: string;
|
||||
severityOverride: string;
|
||||
riskScoreOverride: number;
|
||||
}
|
||||
): RACAlert => {
|
||||
const parents = docs.map(buildParent);
|
||||
const depth = parents.reduce((acc, parent) => Math.max(parent.depth, acc), 0) + 1;
|
||||
const ancestors = docs.reduce((acc: Ancestor[], doc) => acc.concat(buildAncestors(doc)), []);
|
||||
|
||||
const { id, output_index: outputIndex, ...mappedRule } = rule;
|
||||
mappedRule.uuid = id;
|
||||
const { output_index: outputIndex, ...commonRuleParams } = commonParamsCamelToSnake(
|
||||
completeRule.ruleParams
|
||||
);
|
||||
|
||||
const ruleParamsSnakeCase = {
|
||||
...commonRuleParams,
|
||||
...typeSpecificCamelToSnake(completeRule.ruleParams),
|
||||
};
|
||||
|
||||
const {
|
||||
actions,
|
||||
schedule,
|
||||
name,
|
||||
tags,
|
||||
enabled,
|
||||
createdBy,
|
||||
updatedBy,
|
||||
throttle,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
} = completeRule.ruleConfig;
|
||||
|
||||
return {
|
||||
[TIMESTAMP]: new Date().toISOString(),
|
||||
|
@ -106,7 +140,27 @@ export const buildAlert = (
|
|||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_DEPTH]: depth,
|
||||
[ALERT_REASON]: reason,
|
||||
...flattenWithPrefix(ALERT_RULE_NAMESPACE, mappedRule as RulesSchema),
|
||||
[ALERT_BUILDING_BLOCK_TYPE]: completeRule.ruleParams.buildingBlockType,
|
||||
[ALERT_SEVERITY]: overrides?.severityOverride ?? completeRule.ruleParams.severity,
|
||||
[ALERT_RISK_SCORE]: overrides?.riskScoreOverride ?? completeRule.ruleParams.riskScore,
|
||||
[ALERT_RULE_PARAMETERS]: ruleParamsSnakeCase,
|
||||
...flattenWithPrefix(ALERT_RULE_NAMESPACE, {
|
||||
uuid: completeRule.alertId,
|
||||
actions: actions.map(transformAlertToRuleAction),
|
||||
created_at: createdAt.toISOString(),
|
||||
created_by: createdBy ?? '',
|
||||
enabled,
|
||||
interval: schedule.interval,
|
||||
name: overrides?.nameOverride ?? name,
|
||||
tags: transformTags(tags),
|
||||
throttle: throttle ?? undefined,
|
||||
updated_at: updatedAt.toISOString(),
|
||||
updated_by: updatedBy ?? '',
|
||||
type: completeRule.ruleParams.type,
|
||||
...commonRuleParams,
|
||||
severity: overrides?.severityOverride ?? completeRule.ruleParams.severity,
|
||||
risk_score: overrides?.riskScoreOverride ?? completeRule.ruleParams.riskScore,
|
||||
}),
|
||||
} as unknown as RACAlert;
|
||||
};
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@ describe('buildAlert', () => {
|
|||
},
|
||||
]),
|
||||
[ALERT_DEPTH]: 2,
|
||||
[ALERT_BUILDING_BLOCK_TYPE]: 'default',
|
||||
[ALERT_RULE_CONSUMER]: SERVER_APP_ID,
|
||||
}),
|
||||
})
|
||||
|
|
|
@ -102,7 +102,7 @@ export const buildAlertRoot = (
|
|||
const rule = buildRuleWithoutOverrides(completeRule);
|
||||
const mergedAlerts = objectArrayIntersection(wrappedBuildingBlocks.map((alert) => alert._source));
|
||||
const reason = buildReasonMessage({ rule, mergedDoc: mergedAlerts as SignalSourceHit });
|
||||
const doc = buildAlert(wrappedBuildingBlocks, rule, spaceId, reason);
|
||||
const doc = buildAlert(wrappedBuildingBlocks, completeRule, spaceId, reason);
|
||||
return {
|
||||
...mergedAlerts,
|
||||
event: {
|
||||
|
@ -110,7 +110,6 @@ export const buildAlertRoot = (
|
|||
},
|
||||
...doc,
|
||||
[ALERT_ORIGINAL_TIME]: timestamps[0],
|
||||
[ALERT_BUILDING_BLOCK_TYPE]: undefined,
|
||||
[ALERT_GROUP_ID]: generateAlertId(doc),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -18,6 +18,9 @@ import { RACAlert } from '../../types';
|
|||
import { additionalAlertFields, buildAlert } from './build_alert';
|
||||
import { filterSource } from './filter_source';
|
||||
import { CompleteRule, RuleParams } from '../../../schemas/rule_schemas';
|
||||
import { buildRuleNameFromMapping } from '../../../signals/mappings/build_rule_name_from_mapping';
|
||||
import { buildSeverityFromMapping } from '../../../signals/mappings/build_severity_from_mapping';
|
||||
import { buildRiskScoreFromMapping } from '../../../signals/mappings/build_risk_score_from_mapping';
|
||||
|
||||
const isSourceDoc = (
|
||||
hit: SignalSourceHit
|
||||
|
@ -58,11 +61,31 @@ export const buildBulkBody = (
|
|||
const filteredSource = filterSource(mergedDoc);
|
||||
const reason = buildReasonMessage({ mergedDoc, rule });
|
||||
|
||||
const overrides = applyOverrides
|
||||
? {
|
||||
nameOverride: buildRuleNameFromMapping({
|
||||
eventSource: mergedDoc._source ?? {},
|
||||
ruleName: completeRule.ruleConfig.name,
|
||||
ruleNameMapping: completeRule.ruleParams.ruleNameOverride,
|
||||
}).ruleName,
|
||||
severityOverride: buildSeverityFromMapping({
|
||||
eventSource: mergedDoc._source ?? {},
|
||||
severity: completeRule.ruleParams.severity,
|
||||
severityMapping: completeRule.ruleParams.severityMapping,
|
||||
}).severity,
|
||||
riskScoreOverride: buildRiskScoreFromMapping({
|
||||
eventSource: mergedDoc._source ?? {},
|
||||
riskScore: completeRule.ruleParams.riskScore,
|
||||
riskScoreMapping: completeRule.ruleParams.riskScoreMapping,
|
||||
}).riskScore,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
if (isSourceDoc(mergedDoc)) {
|
||||
return {
|
||||
...filteredSource,
|
||||
...eventFields,
|
||||
...buildAlert([mergedDoc], rule, spaceId, reason),
|
||||
...buildAlert([mergedDoc], completeRule, spaceId, reason, overrides),
|
||||
...additionalAlertFields({ ...mergedDoc, _source: { ...mergedDoc._source, ...eventFields } }),
|
||||
[EVENT_KIND]: 'signal',
|
||||
[TIMESTAMP]: new Date().toISOString(),
|
||||
|
|
|
@ -6,10 +6,8 @@
|
|||
*/
|
||||
|
||||
import { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import {
|
||||
ALERT_RULE_THRESHOLD_FIELD,
|
||||
ALERT_ORIGINAL_TIME,
|
||||
} from '../../../../../common/field_maps/field_names';
|
||||
import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils';
|
||||
import { ALERT_ORIGINAL_TIME } from '../../../../../common/field_maps/field_names';
|
||||
|
||||
import { SimpleHit, ThresholdSignalHistory } from '../types';
|
||||
import { getThresholdTermsHash, isWrappedRACAlert, isWrappedSignalHit } from '../utils';
|
||||
|
@ -20,7 +18,11 @@ interface GetThresholdSignalHistoryParams {
|
|||
|
||||
const getTerms = (alert: SimpleHit) => {
|
||||
if (isWrappedRACAlert(alert)) {
|
||||
return (alert._source[ALERT_RULE_THRESHOLD_FIELD] as string[]).map((field) => ({
|
||||
const parameters = alert._source[ALERT_RULE_PARAMETERS] as unknown as Record<
|
||||
string,
|
||||
Record<string, string[]>
|
||||
>;
|
||||
return parameters.threshold.field.map((field) => ({
|
||||
field,
|
||||
value: alert._source[field] as string,
|
||||
}));
|
||||
|
|
|
@ -9,7 +9,10 @@ import expect from '@kbn/expect';
|
|||
import {
|
||||
ALERT_REASON,
|
||||
ALERT_RULE_NAMESPACE,
|
||||
ALERT_RULE_PARAMETERS,
|
||||
ALERT_RULE_UPDATED_AT,
|
||||
ALERT_SEVERITY,
|
||||
ALERT_RISK_SCORE,
|
||||
ALERT_STATUS,
|
||||
ALERT_UUID,
|
||||
ALERT_WORKFLOW_STATUS,
|
||||
|
@ -157,6 +160,29 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
[ALERT_STATUS]: 'active',
|
||||
[SPACE_IDS]: ['default'],
|
||||
[TAGS]: [`__internal_rule_id:${createdRule.rule_id}`, '__internal_immutable:false'],
|
||||
[ALERT_SEVERITY]: 'critical',
|
||||
[ALERT_RISK_SCORE]: 50,
|
||||
[ALERT_RULE_PARAMETERS]: {
|
||||
anomaly_threshold: 30,
|
||||
author: [],
|
||||
description: 'Test ML rule description',
|
||||
exceptions_list: [],
|
||||
false_positives: [],
|
||||
from: '1900-01-01T00:00:00.000Z',
|
||||
immutable: false,
|
||||
machine_learning_job_id: ['linux_anomalous_network_activity_ecs'],
|
||||
max_signals: 100,
|
||||
references: [],
|
||||
risk_score: 50,
|
||||
risk_score_mapping: [],
|
||||
rule_id: createdRule.rule_id,
|
||||
severity: 'critical',
|
||||
severity_mapping: [],
|
||||
threat: [],
|
||||
to: 'now',
|
||||
type: 'machine_learning',
|
||||
version: 1,
|
||||
},
|
||||
...flattenWithPrefix(ALERT_RULE_NAMESPACE, {
|
||||
uuid: createdRule.id,
|
||||
category: 'Machine Learning Rule',
|
||||
|
@ -189,8 +215,6 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
exceptions_list: [],
|
||||
immutable: false,
|
||||
type: 'machine_learning',
|
||||
anomaly_threshold: 30,
|
||||
machine_learning_job_id: ['linux_anomalous_network_activity_ecs'],
|
||||
}),
|
||||
[ALERT_DEPTH]: 1,
|
||||
[ALERT_REASON]: `event with process store, by root on mothra created critical alert Test ML rule.`,
|
||||
|
|
|
@ -302,13 +302,10 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
false_positives: [],
|
||||
from: '1900-01-01T00:00:00.000Z',
|
||||
immutable: false,
|
||||
index: ['auditbeat-*'],
|
||||
interval: '5m',
|
||||
language: 'kuery',
|
||||
max_signals: 100,
|
||||
name: 'Query with a rule id',
|
||||
producer: 'siem',
|
||||
query: '*:*',
|
||||
references: [],
|
||||
risk_score: 55,
|
||||
risk_score_mapping: [],
|
||||
|
@ -318,20 +315,6 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
severity_mapping: [],
|
||||
tags: [],
|
||||
threat: [],
|
||||
threat_filters: [],
|
||||
threat_index: ['auditbeat-*'],
|
||||
threat_mapping: [
|
||||
{
|
||||
entries: [
|
||||
{
|
||||
field: 'host.name',
|
||||
type: 'mapping',
|
||||
value: 'host.name',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
threat_query: 'source.ip: "188.166.120.93"',
|
||||
to: 'now',
|
||||
type: 'threat_match',
|
||||
updated_at: fullSignal[ALERT_RULE_UPDATED_AT],
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import expect from '@kbn/expect';
|
||||
import {
|
||||
ALERT_REASON,
|
||||
ALERT_RISK_SCORE,
|
||||
ALERT_RULE_NAME,
|
||||
ALERT_RULE_RISK_SCORE,
|
||||
ALERT_RULE_RISK_SCORE_MAPPING,
|
||||
|
@ -16,6 +17,7 @@ import {
|
|||
ALERT_RULE_SEVERITY,
|
||||
ALERT_RULE_SEVERITY_MAPPING,
|
||||
ALERT_RULE_UUID,
|
||||
ALERT_SEVERITY,
|
||||
ALERT_WORKFLOW_STATUS,
|
||||
EVENT_ACTION,
|
||||
EVENT_KIND,
|
||||
|
@ -1000,7 +1002,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const signals = await executeRuleAndGetSignals(rule);
|
||||
const severities = signals.map((s) => ({
|
||||
id: (s?.[ALERT_ANCESTORS] as Ancestor[])[0].id,
|
||||
value: s?.[ALERT_RULE_SEVERITY],
|
||||
value: s?.[ALERT_SEVERITY],
|
||||
}));
|
||||
|
||||
expect(signals.length).equal(4);
|
||||
|
@ -1034,7 +1036,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const signals = await executeRuleAndGetSignals(rule);
|
||||
const riskScores = signals.map((s) => ({
|
||||
id: (s?.[ALERT_ANCESTORS] as Ancestor[])[0].id,
|
||||
value: s?.[ALERT_RULE_RISK_SCORE],
|
||||
value: s?.[ALERT_RISK_SCORE],
|
||||
}));
|
||||
|
||||
expect(signals.length).equal(4);
|
||||
|
@ -1071,8 +1073,8 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const signals = await executeRuleAndGetSignals(rule);
|
||||
const values = signals.map((s) => ({
|
||||
id: (s?.[ALERT_ANCESTORS] as Ancestor[])[0].id,
|
||||
severity: s?.[ALERT_RULE_SEVERITY],
|
||||
risk: s?.[ALERT_RULE_RISK_SCORE],
|
||||
severity: s?.[ALERT_SEVERITY],
|
||||
risk: s?.[ALERT_RISK_SCORE],
|
||||
}));
|
||||
|
||||
expect(signals.length).equal(4);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue