From c316708393e5c5460862bfce2db193800a9ff334 Mon Sep 17 00:00:00 2001 From: Devin Hurley Date: Tue, 26 Nov 2019 16:21:07 -0500 Subject: [PATCH 1/3] allows addition of ecs threat properties to rules and signals for mitre attack info --- .../alerts/__mocks__/es_results.ts | 1 + .../detection_engine/alerts/create_rules.ts | 2 + .../alerts/rules_alert_type.ts | 1 + .../lib/detection_engine/alerts/types.ts | 14 ++ .../detection_engine/alerts/update_rules.ts | 2 + .../lib/detection_engine/alerts/utils.ts | 1 + .../routes/__mocks__/request_responses.ts | 22 +++ .../routes/create_rules_route.ts | 3 +- .../detection_engine/routes/schemas.test.ts | 149 +++++++++++++++++- .../lib/detection_engine/routes/schemas.ts | 27 ++++ .../routes/update_rules_route.ts | 2 + .../lib/detection_engine/routes/utils.test.ts | 135 ++++++++++++++++ .../lib/detection_engine/routes/utils.ts | 1 + .../lib/detection_engine/signals_mapping.json | 5 +- 14 files changed, 362 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/__mocks__/es_results.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/__mocks__/es_results.ts index 8080bd5ddd9139..bed466dd9b94f1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/__mocks__/es_results.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/__mocks__/es_results.ts @@ -35,6 +35,7 @@ export const sampleRuleAlertParams = ( filters: undefined, savedId: undefined, meta: undefined, + threats: undefined, }); export const sampleDocNoSortId = (someUuid: string = sampleIdGuid): SignalSourceHit => ({ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/create_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/create_rules.ts index 7c66714484383f..4418bbc52b57d6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/create_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/create_rules.ts @@ -30,6 +30,7 @@ export const createRules = async ({ name, severity, tags, + threats, to, type, references, @@ -57,6 +58,7 @@ export const createRules = async ({ riskScore, severity, tags, + threats, to, type, references, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/rules_alert_type.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/rules_alert_type.ts index 91d7d18a4945cd..61fe9c7c226395 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/rules_alert_type.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/rules_alert_type.ts @@ -48,6 +48,7 @@ export const rulesAlertType = ({ riskScore: schema.number(), severity: schema.string(), tags: schema.arrayOf(schema.string(), { defaultValue: [] }), + threats: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), to: schema.string(), type: schema.string(), references: schema.arrayOf(schema.string(), { defaultValue: [] }), diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts index 462a9b7d65ee22..c17e01f056f81b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts @@ -21,6 +21,19 @@ import { esFilters } from '../../../../../../../../src/plugins/data/server'; export type PartialFilter = Partial; +export interface ThreatParams { + framework: string; + tactic: { + id: string; + name: string; + reference: string; + }; + technique: { + id: string; + name: string; + reference: string; + }; +} export interface RuleAlertParams { description: string; enabled: boolean; @@ -44,6 +57,7 @@ export interface RuleAlertParams { severity: string; tags: string[]; to: string; + threats: ThreatParams[] | undefined | null; type: 'filter' | 'query' | 'saved_query'; } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/update_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/update_rules.ts index 81360d78242302..d6b828642433de 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/update_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/update_rules.ts @@ -65,6 +65,7 @@ export const updateRules = async ({ name, severity, tags, + threats, to, type, references, @@ -101,6 +102,7 @@ export const updateRules = async ({ riskScore, severity, tags, + threats, to, type, references, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.ts index c3988b8fea458c..9dedda6d79839e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.ts @@ -64,6 +64,7 @@ export const buildRule = ({ filters: ruleParams.filters, created_by: createdBy, updated_by: updatedBy, + threats: ruleParams.threats, }); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 4c49326fbb32a9..cf76987aa38ad2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -26,6 +26,13 @@ export const typicalPayload = (): Partial> = severity: 'high', query: 'user.name: root or user.name: admin', language: 'kuery', + threats: [ + { + framework: 'fake', + tactic: { id: 'fakeId', name: 'fakeName', reference: 'fakeRef' }, + technique: { id: 'techniqueId', name: 'techniqueName', reference: 'techniqueRef' }, + }, + ], }); export const typicalFilterPayload = (): Partial => ({ @@ -139,6 +146,21 @@ export const getResult = (): RuleAlertType => ({ tags: [], to: 'now', type: 'query', + threats: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0040', + name: 'impact', + reference: 'https://attack.mitre.org/tactics/TA0040/', + }, + technique: { + id: 'T1499', + name: 'endpoint denial of service', + reference: 'https://attack.mitre.org/techniques/T1499/', + }, + }, + ], references: ['http://www.example.com', 'https://ww.example.com'], }, interval: '5m', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/create_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/create_rules_route.ts index 7e1ac07e1f0aaa..4ff3a9b96b93e6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/create_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/create_rules_route.ts @@ -50,11 +50,11 @@ export const createCreateRulesRoute: Hapi.ServerRoute = { name, severity, tags, + threats, to, type, references, } = request.payload; - const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; const actionsClient = isFunction(request.getActionsClient) ? request.getActionsClient() : null; @@ -94,6 +94,7 @@ export const createCreateRulesRoute: Hapi.ServerRoute = { tags, to, type, + threats, references, }); return transformOrError(createdRule); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.test.ts index 6c7e5c4054326d..e26f2b842da0b5 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.test.ts @@ -5,7 +5,12 @@ */ import { createRulesSchema, updateRulesSchema, findRulesSchema, queryRulesSchema } from './schemas'; -import { RuleAlertParamsRest, FindParamsRest, UpdateRuleAlertParamsRest } from '../alerts/types'; +import { + RuleAlertParamsRest, + FindParamsRest, + UpdateRuleAlertParamsRest, + ThreatParams, +} from '../alerts/types'; describe('schemas', () => { describe('create rules schema', () => { @@ -240,6 +245,39 @@ describe('schemas', () => { }).error ).toBeFalsy(); }); + test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threats] does validate', () => { + expect( + createRulesSchema.validate>({ + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'filter', + filter: {}, + threats: [ + { + framework: 'someFramework', + tactic: { + id: 'fakeId', + name: 'fakeName', + reference: 'fakeRef', + }, + technique: { + id: 'techniqueId', + name: 'techniqueName', + reference: 'techniqueRef', + }, + }, + ], + }).error + ).toBeFalsy(); + }); test('If filter type is set then filter is required', () => { expect( @@ -735,6 +773,115 @@ describe('schemas', () => { ).error ).toBeTruthy(); }); + test('You cannot send in an array of threats that are missing "framework"', () => { + expect( + createRulesSchema.validate< + Partial> & { + threats: Array>>; + } + >({ + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [ + { + tactic: { + id: 'fakeId', + name: 'fakeName', + reference: 'fakeRef', + }, + technique: { + id: 'techniqueId', + name: 'techniqueName', + reference: 'techniqueRef', + }, + }, + ], + }).error + ).toBeTruthy(); + }); + test('You cannot send in an array of threats that are missing "tactic"', () => { + expect( + createRulesSchema.validate< + Partial> & { + threats: Array>>; + } + >({ + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [ + { + framework: 'fake', + technique: { + id: 'techniqueId', + name: 'techniqueName', + reference: 'techniqueRef', + }, + }, + ], + }).error + ).toBeTruthy(); + }); + test('You cannot send in an array of threats that are missing "techniques"', () => { + expect( + createRulesSchema.validate< + Partial> & { + threats: Array>>; + } + >({ + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [ + { + framework: 'fake', + tactic: { + id: 'fakeId', + name: 'fakeName', + reference: 'fakeRef', + }, + }, + ], + }).error + ).toBeTruthy(); + }); test('You can optionally send in an array of false positives', () => { expect( diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.ts index 664a98ad7d7ddd..ff3b9d154eb6c4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.ts @@ -50,6 +50,31 @@ const tags = Joi.array().items(Joi.string()); const fields = Joi.array() .items(Joi.string()) .single(); +const threat_framework = Joi.string(); +const threat_tactic_id = Joi.string(); +const threat_tactic_name = Joi.string(); +const threat_tactic_reference = Joi.string(); +const threat_tactic = Joi.object({ + id: threat_tactic_id.required(), + name: threat_tactic_name.required(), + reference: threat_tactic_reference.required(), +}); +const threat_technique_id = Joi.string(); +const threat_technique_name = Joi.string(); +const threat_technique_reference = Joi.string(); +const threat_technique = Joi.object({ + id: threat_technique_id.required(), + name: threat_technique_name.required(), + reference: threat_technique_reference.required(), +}); + +const threats = Joi.array().items( + Joi.object({ + framework: threat_framework.required(), + tactic: threat_tactic.required(), + technique: threat_technique.required(), + }) +); /* eslint-enable @typescript-eslint/camelcase */ export const createRulesSchema = Joi.object({ @@ -110,6 +135,7 @@ export const createRulesSchema = Joi.object({ tags: tags.default([]), to: to.required(), type: type.required(), + threats, references: references.default([]), }); @@ -165,6 +191,7 @@ export const updateRulesSchema = Joi.object({ tags, to, type, + threats: threats.optional(), references, }).xor('id', 'rule_id'); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/update_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/update_rules_route.ts index 1cc65054527c09..c5fb1675fb3430 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/update_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/update_rules_route.ts @@ -50,6 +50,7 @@ export const createUpdateRulesRoute: Hapi.ServerRoute = { tags, to, type, + threats, references, } = request.payload; @@ -86,6 +87,7 @@ export const createUpdateRulesRoute: Hapi.ServerRoute = { tags, to, type, + threats, references, }); if (rule != null) { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts index 632778d78dab7d..4ef2d87d1d736b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts @@ -39,6 +39,21 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], + threats: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0040', + name: 'impact', + reference: 'https://attack.mitre.org/tactics/TA0040/', + }, + technique: { + id: 'T1499', + name: 'endpoint denial of service', + reference: 'https://attack.mitre.org/techniques/T1499/', + }, + }, + ], to: 'now', type: 'query', }); @@ -66,6 +81,21 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], + threats: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0040', + name: 'impact', + reference: 'https://attack.mitre.org/tactics/TA0040/', + }, + technique: { + id: 'T1499', + name: 'endpoint denial of service', + reference: 'https://attack.mitre.org/techniques/T1499/', + }, + }, + ], to: 'now', type: 'query', }); @@ -95,6 +125,21 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], + threats: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0040', + name: 'impact', + reference: 'https://attack.mitre.org/tactics/TA0040/', + }, + technique: { + id: 'T1499', + name: 'endpoint denial of service', + reference: 'https://attack.mitre.org/techniques/T1499/', + }, + }, + ], to: 'now', type: 'query', }); @@ -124,6 +169,21 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], + threats: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0040', + name: 'impact', + reference: 'https://attack.mitre.org/tactics/TA0040/', + }, + technique: { + id: 'T1499', + name: 'endpoint denial of service', + reference: 'https://attack.mitre.org/techniques/T1499/', + }, + }, + ], to: 'now', type: 'query', }); @@ -151,6 +211,21 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], + threats: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0040', + name: 'impact', + reference: 'https://attack.mitre.org/tactics/TA0040/', + }, + technique: { + id: 'T1499', + name: 'endpoint denial of service', + reference: 'https://attack.mitre.org/techniques/T1499/', + }, + }, + ], to: 'now', type: 'query', }); @@ -181,6 +256,21 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], + threats: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0040', + name: 'impact', + reference: 'https://attack.mitre.org/tactics/TA0040/', + }, + technique: { + id: 'T1499', + name: 'endpoint denial of service', + reference: 'https://attack.mitre.org/techniques/T1499/', + }, + }, + ], to: 'now', type: 'query', }); @@ -211,6 +301,21 @@ describe('utils', () => { severity: 'high', updated_by: 'elastic', tags: [], + threats: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0040', + name: 'impact', + reference: 'https://attack.mitre.org/tactics/TA0040/', + }, + technique: { + id: 'T1499', + name: 'endpoint denial of service', + reference: 'https://attack.mitre.org/techniques/T1499/', + }, + }, + ], to: 'now', type: 'query', }); @@ -294,6 +399,21 @@ describe('utils', () => { tags: [], to: 'now', type: 'query', + threats: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0040', + name: 'impact', + reference: 'https://attack.mitre.org/tactics/TA0040/', + }, + technique: { + id: 'T1499', + name: 'endpoint denial of service', + reference: 'https://attack.mitre.org/techniques/T1499/', + }, + }, + ], }, ], }); @@ -331,6 +451,21 @@ describe('utils', () => { tags: [], to: 'now', type: 'query', + threats: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0040', + name: 'impact', + reference: 'https://attack.mitre.org/tactics/TA0040/', + }, + technique: { + id: 'T1499', + name: 'endpoint denial of service', + reference: 'https://attack.mitre.org/techniques/T1499/', + }, + }, + ], }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts index eb0ae49436bcaf..947fb27a89c3a8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts @@ -54,6 +54,7 @@ export const transformAlertToRule = (alert: RuleAlertType): Partial Date: Tue, 26 Nov 2019 18:52:04 -0500 Subject: [PATCH 2/3] adds default empty array to threats on creation of rule, removes optional from update rules schema as it is implied, updates and adds relevant tests --- .../detection_engine/routes/schemas.test.ts | 215 ++++++++++++++++++ .../lib/detection_engine/routes/schemas.ts | 4 +- .../lib/detection_engine/signals_mapping.json | 2 +- 3 files changed, 218 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.test.ts index e26f2b842da0b5..3c85618452d8cd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.test.ts @@ -245,6 +245,28 @@ describe('schemas', () => { }).error ).toBeFalsy(); }); + test('You can send in an empty array to threats', () => { + expect( + createRulesSchema.validate>({ + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [], + }).error + ).toBeFalsy(); + }); test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threats] does validate', () => { expect( createRulesSchema.validate>({ @@ -773,6 +795,7 @@ describe('schemas', () => { ).error ).toBeTruthy(); }); + test('You cannot send in an array of threats that are missing "framework"', () => { expect( createRulesSchema.validate< @@ -1957,6 +1980,198 @@ describe('schemas', () => { }).error ).toBeTruthy(); }); + + test('threats is not defaulted to empty array on update', () => { + expect( + updateRulesSchema.validate>({ + id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + }).value.threats + ).toBe(undefined); + }); + }); + test('threats is not defaulted to undefined on update with empty array', () => { + expect( + updateRulesSchema.validate>({ + id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [], + }).value.threats + ).toMatchObject([]); + }); + test('threats is valid when updated with all sub-objects', () => { + const expected: ThreatParams[] = [ + { + framework: 'fake', + tactic: { + id: 'fakeId', + name: 'fakeName', + reference: 'fakeRef', + }, + technique: { + id: 'techniqueId', + name: 'techniqueName', + reference: 'techniqueRef', + }, + }, + ]; + expect( + updateRulesSchema.validate>({ + id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [ + { + framework: 'fake', + tactic: { + id: 'fakeId', + name: 'fakeName', + reference: 'fakeRef', + }, + technique: { + id: 'techniqueId', + name: 'techniqueName', + reference: 'techniqueRef', + }, + }, + ], + }).value.threats + ).toMatchObject(expected); + }); + test('threats is invalid when updated with missing property framework', () => { + expect( + updateRulesSchema.validate< + Partial> & { + threats: Array>>; + } + >({ + id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [ + { + tactic: { + id: 'fakeId', + name: 'fakeName', + reference: 'fakeRef', + }, + technique: { + id: 'techniqueId', + name: 'techniqueName', + reference: 'techniqueRef', + }, + }, + ], + }).error + ).toBeTruthy(); + }); + test('threats is invalid when updated with missing tactic sub-object', () => { + expect( + updateRulesSchema.validate< + Partial> & { + threats: Array>>; + } + >({ + id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [ + { + framework: 'fake', + technique: { + id: 'techniqueId', + name: 'techniqueName', + reference: 'techniqueRef', + }, + }, + ], + }).error + ).toBeTruthy(); + }); + test('threats is invalid when updated with missing technique sub-object', () => { + expect( + updateRulesSchema.validate< + Partial> & { + threats: Array>>; + } + >({ + id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + threats: [ + { + framework: 'fake', + tactic: { + id: 'techniqueId', + name: 'techniqueName', + reference: 'techniqueRef', + }, + }, + ], + }).error + ).toBeTruthy(); }); describe('find rules schema', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.ts index ff3b9d154eb6c4..0b4f1094549a4d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.ts @@ -135,7 +135,7 @@ export const createRulesSchema = Joi.object({ tags: tags.default([]), to: to.required(), type: type.required(), - threats, + threats: threats.default([]), references: references.default([]), }); @@ -191,7 +191,7 @@ export const updateRulesSchema = Joi.object({ tags, to, type, - threats: threats.optional(), + threats, references, }).xor('id', 'rule_id'); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals_mapping.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals_mapping.json index 4a9f3b31ec9ae2..94f251645d3fd6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals_mapping.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals_mapping.json @@ -1830,7 +1830,7 @@ "ignore_above": 1024, "type": "keyword" }, - "threats": { + "threat": { "properties": { "framework": { "ignore_above": 1024, From b3045c73c71f360e13fdeadf8d8edfa051a5be44 Mon Sep 17 00:00:00 2001 From: Devin Hurley Date: Tue, 26 Nov 2019 19:17:27 -0500 Subject: [PATCH 3/3] adds sample rule with mitre attack threats property --- .../scripts/rules/root_or_admin_threats.json | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/root_or_admin_threats.json diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/root_or_admin_threats.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/root_or_admin_threats.json new file mode 100644 index 00000000000000..845e6e17c6498c --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/root_or_admin_threats.json @@ -0,0 +1,43 @@ +{ + "rule_id": "rule-1", + "description": "Detecting root and admin users", + "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], + "interval": "5s", + "name": "Detect Root/Admin Users", + "severity": "high", + "risk_score": 1, + "type": "query", + "from": "now-6s", + "to": "now", + "query": "user.name: root or user.name: admin", + "language": "kuery", + "references": ["http://www.example.com", "https://ww.example.com"], + "threats": [ + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "TA0040", + "name": "impact", + "reference": "https://attack.mitre.org/tactics/TA0040/" + }, + "technique": { + "id": "T1499", + "name": "endpoint denial of service", + "reference": "https://attack.mitre.org/techniques/T1499/" + } + }, + { + "framework": "MITRE ATT&CK", + "tactic": { + "id": "T1020", + "name": "Automated Exfiltration", + "reference": "https://attack.mitre.org/techniques/T1020/" + }, + "technique": { + "id": "T1002", + "name": "Data Compressed", + "reference": "https://attack.mitre.org/techniques/T1002/" + } + } + ] +}