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 3c1a01fd58c60..54ed42a1d2b6c 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 @@ -59,6 +59,7 @@ export const mockPrepackagedRule = (): PrepackagedRules => ({ version: 1, false_positives: [], max_signals: 100, + note: '', timeline_id: 'timeline-id', timeline_title: 'timeline-title', }); @@ -392,6 +393,7 @@ export const getResult = (): RuleAlertType => ({ }, ], references: ['http://www.example.com', 'https://ww.example.com'], + note: '# Investigative notes', version: 1, }, createdAt: new Date('2019-12-13T16:40:33.400Z'), diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json index 714b39d1557a1..dc20f0793a6f8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/index/signals_mapping.json @@ -133,6 +133,9 @@ } } }, + "note": { + "type": "text" + }, "type": { "type": "keyword" }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index ee8539faacf3e..d727bbb953d2a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -78,6 +78,7 @@ export const createRulesBulkRoute = (router: IRouter) => { to, type, references, + note, timeline_id: timelineId, timeline_title: timelineTitle, version, @@ -131,6 +132,7 @@ export const createRulesBulkRoute = (router: IRouter) => { type, threat, references, + note, version, }); return transformValidateBulkError(ruleIdOrUuid, createdRule); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts index cef7ded2b50b4..fcfcee99f369e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -55,6 +55,7 @@ export const createRulesRoute = (router: IRouter): void => { to, type, references, + note, } = request.body; const siemResponse = buildSiemResponse(response); @@ -117,6 +118,7 @@ export const createRulesRoute = (router: IRouter): void => { type, threat, references, + note, version: 1, }); const ruleStatuses = await savedObjectsClient.find< diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts index d9fc9b4e3c04f..ec4e707f46e50 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -134,6 +134,7 @@ export const importRulesRoute = (router: IRouter, config: LegacyServices['config to, type, references, + note, timeline_id: timelineId, timeline_title: timelineTitle, version, @@ -183,6 +184,7 @@ export const importRulesRoute = (router: IRouter, config: LegacyServices['config type, threat, references, + note, version, }); resolve({ rule_id: ruleId, status_code: 200 }); @@ -217,6 +219,7 @@ export const importRulesRoute = (router: IRouter, config: LegacyServices['config type, threat, references, + note, version, }); resolve({ rule_id: ruleId, status_code: 200 }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 7ca16a75fb562..e64bbe625f5f6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -71,6 +71,7 @@ export const patchRulesBulkRoute = (router: IRouter) => { type, threat, references, + note, version, } = payloadRule; const idOrRuleIdOrUnknown = id ?? ruleId ?? '(unknown id)'; @@ -104,6 +105,7 @@ export const patchRulesBulkRoute = (router: IRouter) => { type, threat, references, + note, version, }); if (rule != null) { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts index dce5f4037db1c..2d810d33c6e51 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -55,6 +55,7 @@ export const patchRulesRoute = (router: IRouter) => { type, threat, references, + note, version, } = request.body; const siemResponse = buildSiemResponse(response); @@ -101,6 +102,7 @@ export const patchRulesRoute = (router: IRouter) => { type, threat, references, + note, version, }); if (rule != null) { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index 953fb16d26ac6..777b9f3cc7a9d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -72,6 +72,7 @@ export const updateRulesBulkRoute = (router: IRouter) => { type, threat, references, + note, version, } = payloadRule; const finalIndex = outputIndex ?? siemClient.signalsIndex; @@ -107,6 +108,7 @@ export const updateRulesBulkRoute = (router: IRouter) => { type, threat, references, + note, version, }); if (rule != null) { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts index fbb930d780f01..1393de8c725cb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -55,6 +55,7 @@ export const updateRulesRoute = (router: IRouter) => { type, threat, references, + note, version, } = request.body; const siemResponse = buildSiemResponse(response); @@ -103,6 +104,7 @@ export const updateRulesRoute = (router: IRouter) => { type, threat, references, + note, version, }); if (rule != null) { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts index 25f0151923e2e..70fcbb2c163ca 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.test.ts @@ -92,6 +92,7 @@ describe('utils', () => { timeline_title: 'some-timeline-title', to: 'now', type: 'query', + note: '# Investigative notes', version: 1, }; expect(rule).toEqual(expected); @@ -154,6 +155,7 @@ describe('utils', () => { timeline_title: 'some-timeline-title', to: 'now', type: 'query', + note: '# Investigative notes', version: 1, }; expect(omitData).toEqual(expected); @@ -218,6 +220,7 @@ describe('utils', () => { timeline_title: 'some-timeline-title', to: 'now', type: 'query', + note: '# Investigative notes', version: 1, }; expect(rule).toEqual(expected); @@ -282,6 +285,7 @@ describe('utils', () => { timeline_title: 'some-timeline-title', to: 'now', type: 'query', + note: '# Investigative notes', version: 1, }; expect(rule).toEqual(expected); @@ -344,6 +348,7 @@ describe('utils', () => { timeline_title: 'some-timeline-title', to: 'now', type: 'query', + note: '# Investigative notes', version: 1, }; expect(omitData).toEqual(expected); @@ -409,6 +414,7 @@ describe('utils', () => { timeline_title: 'some-timeline-title', to: 'now', type: 'query', + note: '# Investigative notes', version: 1, }; expect(ruleWithEnabledFalse).toEqual(expected); @@ -474,6 +480,7 @@ describe('utils', () => { timeline_title: 'some-timeline-title', to: 'now', type: 'query', + note: '# Investigative notes', version: 1, }; expect(ruleWithEnabledFalse).toEqual(expected); @@ -539,6 +546,7 @@ describe('utils', () => { timeline_title: 'some-timeline-title', to: 'now', type: 'query', + note: '# Investigative notes', version: 1, }; expect(rule).toEqual(expected); @@ -688,6 +696,7 @@ describe('utils', () => { }, timeline_id: 'some-timeline-id', timeline_title: 'some-timeline-title', + note: '# Investigative notes', version: 1, }; expect(output).toEqual({ @@ -769,6 +778,7 @@ describe('utils', () => { }, timeline_id: 'some-timeline-id', timeline_title: 'some-timeline-title', + note: '# Investigative notes', version: 1, }; expect(output).toEqual(expected); @@ -941,6 +951,7 @@ describe('utils', () => { }, timeline_id: 'some-timeline-id', timeline_title: 'some-timeline-title', + note: '# Investigative notes', version: 1, }; expect(output).toEqual(expected); @@ -1053,6 +1064,7 @@ describe('utils', () => { type: 'query', updated_at: '2019-12-13T16:40:33.400Z', updated_by: 'elastic', + note: '# Investigative notes', version: 1, }, ]); @@ -1112,6 +1124,7 @@ describe('utils', () => { type: 'query', updated_at: '2019-12-13T16:40:33.400Z', updated_by: 'elastic', + note: '# Investigative notes', version: 1, }, { @@ -1160,6 +1173,7 @@ describe('utils', () => { type: 'query', updated_at: '2019-12-13T16:40:33.400Z', updated_by: 'elastic', + note: '# Investigative notes', version: 1, }, ]); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts index 064bd8315969e..ecf669b0106c3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts @@ -131,6 +131,7 @@ export const transformAlertToRule = ( to: alert.params.to, type: alert.params.type, threat: alert.params.threat, + note: alert.params.note, version: alert.params.version, status: ruleStatus?.attributes.status, status_date: ruleStatus?.attributes.statusDate, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts index 812552aef0ed8..ba6c702e9601b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts @@ -72,6 +72,7 @@ export const ruleOutput: RulesSchema = { meta: { someMeta: 'someField', }, + note: '# Investigative notes', timeline_title: 'some-timeline-title', timeline_id: 'some-timeline-id', }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts index b536cfac05df3..a002cc9324012 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts @@ -1274,4 +1274,62 @@ describe('add prepackaged rules schema', () => { 'child "severity" fails because ["severity" must be one of [low, medium, high, critical]]' ); }); + + describe('note', () => { + test('You can set note to any string you want', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + meta: { + somethingMadeUp: { somethingElse: true }, + }, + note: '# test header', + version: 1, + }).error + ).toBeFalsy(); + }); + + test('You cannot create note as anything other than a string', () => { + expect( + addPrepackagedRulesSchema.validate< + Partial & { note: object }> + >({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + meta: { + somethingMadeUp: { somethingElse: true }, + }, + note: { + somethingMadeUp: { somethingElse: true }, + }, + version: 1, + }).error.message + ).toEqual('child "note" fails because ["note" must be a string]'); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts index b62c480492c84..974ddcf35eeb4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts @@ -32,6 +32,7 @@ import { type, threat, references, + note, version, } from './schemas'; /* eslint-enable @typescript-eslint/camelcase */ @@ -79,5 +80,6 @@ export const addPrepackagedRulesSchema = Joi.object({ type: type.required(), threat: threat.default([]), references: references.default([]), + note: note.allow(''), version: version.required(), }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_bulk_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_bulk_schema.test.ts index 2a64478962ced..6512bfdc4361f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_bulk_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_bulk_schema.test.ts @@ -141,4 +141,71 @@ describe('create_rules_bulk_schema', () => { '"value" at position 0 fails because [child "severity" fails because ["severity" must be one of [low, medium, high, critical]]]' ); }); + + test('You can set "note" to a string', () => { + expect( + createRulesBulkSchema.validate>([ + { + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + name: 'some-name', + severity: 'low', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + note: '# test markdown', + version: 1, + }, + ]).error + ).toBeFalsy(); + }); + + test('You can set "note" to an empty string', () => { + expect( + createRulesBulkSchema.validate>([ + { + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + name: 'some-name', + severity: 'low', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + note: '', + version: 1, + }, + ]).error + ).toBeFalsy(); + }); + + test('You cannot set "note" to anything other than string', () => { + expect( + createRulesBulkSchema.validate>([ + { + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + name: 'some-name', + severity: 'low', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + note: { + something: 'some object', + }, + version: 1, + }, + ]).error.message + ).toEqual( + '"value" at position 0 fails because [child "note" fails because ["note" must be a string]]' + ); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts index 052a149f3d4dc..3bad87dc1a9ad 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts @@ -1224,4 +1224,95 @@ describe('create rules schema', () => { 'child "severity" fails because ["severity" must be one of [low, medium, high, critical]]' ); }); + + describe('note', () => { + test('You can set note to a string', () => { + 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: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + note: '# documentation markdown here', + }).error + ).toBeFalsy(); + }); + + test('You can set note to an emtpy string', () => { + 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: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + note: '', + }).error + ).toBeFalsy(); + }); + + test('You cannot create note as an object', () => { + expect( + createRulesSchema.validate & { note: object }>>({ + 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: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + note: { + somethingHere: 'something else', + }, + }).error.message + ).toEqual('child "note" fails because ["note" must be a string]'); + }); + + test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, note] does validate', () => { + expect( + createRulesSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'low', + interval: '5m', + type: 'query', + risk_score: 50, + note: '# some markdown', + }).error + ).toBeFalsy(); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts index eb79e06c8efa6..c9b380d3c67e1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts @@ -32,6 +32,7 @@ import { type, threat, references, + note, version, } from './schemas'; /* eslint-enable @typescript-eslint/camelcase */ @@ -67,5 +68,6 @@ export const createRulesSchema = Joi.object({ type: type.required(), threat: threat.default([]), references: references.default([]), + note: note.allow(''), version: version.default(1), }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts index da441681de50b..9c80ddde9e7b7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts @@ -1423,4 +1423,116 @@ describe('import rules schema', () => { 'child "severity" fails because ["severity" must be one of [low, medium, high, critical]]' ); }); + + describe('note', () => { + test('You can set note to a string', () => { + expect( + importRulesSchema.validate>({ + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: false, + index: ['index-1'], + name: 'some-name', + severity: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + meta: { + somethingMadeUp: { somethingElse: true }, + }, + note: '# test header', + }).error + ).toBeFalsy(); + }); + + test('You can set note to an empty string', () => { + expect( + importRulesSchema.validate>({ + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: false, + index: ['index-1'], + name: 'some-name', + severity: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + meta: { + somethingMadeUp: { somethingElse: true }, + }, + note: '', + }).error + ).toBeFalsy(); + }); + + test('You cannot create note set to null', () => { + expect( + importRulesSchema.validate>({ + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: false, + index: ['index-1'], + name: 'some-name', + severity: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + meta: { + somethingMadeUp: { somethingElse: true }, + }, + note: null, + }).error.message + ).toEqual('child "note" fails because ["note" must be a string]'); + }); + + test('You cannot create note as something other than a string', () => { + expect( + importRulesSchema.validate & { note: object }>>({ + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + immutable: false, + index: ['index-1'], + name: 'some-name', + severity: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + meta: { + somethingMadeUp: { somethingElse: true }, + }, + note: { + somethingMadeUp: { somethingElse: true }, + }, + }).error.message + ).toEqual('child "note" fails because ["note" must be a string]'); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.ts index 1254694645b9c..bd12872c4dc72 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.ts @@ -38,6 +38,7 @@ import { type, threat, references, + note, version, } from './schemas'; /* eslint-enable @typescript-eslint/camelcase */ @@ -84,6 +85,7 @@ export const importRulesSchema = Joi.object({ type: type.required(), threat: threat.default([]), references: references.default([]), + note: note.allow(''), version: version.default(1), created_at, updated_at, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_bulk_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_bulk_schema.test.ts index cbcb9eba75bc1..43d1e7ab2aa3b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_bulk_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_bulk_schema.test.ts @@ -49,4 +49,43 @@ describe('patch_rules_bulk_schema', () => { ]).error ).toBeFalsy(); }); + + test('can set "note" to be a string', () => { + expect( + patchRulesBulkSchema.validate>>([ + { + id: 'rule-1', + note: 'hi', + }, + ]).error + ).toBeFalsy(); + }); + + test('can set "note" to be an empty string', () => { + expect( + patchRulesBulkSchema.validate>>([ + { + id: 'rule-1', + note: '', + }, + ]).error + ).toBeFalsy(); + }); + + test('cannot set "note" to be anything other than a string', () => { + expect( + patchRulesBulkSchema.validate< + Array & { note: object }>> + >([ + { + id: 'rule-1', + note: { + someprop: 'some value here', + }, + }, + ]).error.message + ).toEqual( + '"value" at position 0 fails because [child "note" fails because ["note" must be a string]]' + ); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.test.ts index 11bed22e1c047..ecdba7ccc0091 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.test.ts @@ -1012,4 +1012,45 @@ describe('patch rules schema', () => { 'child "severity" fails because ["severity" must be one of [low, medium, high, critical]]' ); }); + + describe('note', () => { + test('[rule_id, description, from, to, index, name, severity, interval, type, note] does validate', () => { + expect( + patchRulesSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'low', + interval: '5m', + type: 'query', + note: '# some documentation markdown', + }).error + ).toBeFalsy(); + }); + + test('note can be patched', () => { + expect( + patchRulesSchema.validate>({ + id: 'rule-1', + note: '# new documentation markdown', + }).error + ).toBeFalsy(); + }); + + test('You cannot patch note as an object', () => { + expect( + patchRulesSchema.validate< + Partial & { note: object }> + >({ + id: 'rule-1', + note: { + someProperty: 'something else here', + }, + }).error.message + ).toEqual('child "note" fails because ["note" must be a string]'); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.ts index d0ed1af01833b..4d1b73fb69e5b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.ts @@ -32,6 +32,7 @@ import { type, threat, references, + note, id, version, } from './schemas'; @@ -63,5 +64,6 @@ export const patchRulesSchema = Joi.object({ type, threat, references, + note: note.allow(''), version, }).xor('id', 'rule_id'); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.ts index ae2d6269279e1..945b5651be066 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.ts @@ -48,6 +48,7 @@ import { version, filters, meta, + note, } from './schemas'; /** @@ -113,6 +114,7 @@ export const partialRulesSchema = t.partial({ filters, meta, index, + note, }); /** diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/schemas.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/schemas.ts index 14de14a8464fb..16f6c0fd6b8b4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/schemas.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/schemas.ts @@ -128,3 +128,4 @@ export const success_count = PositiveInteger; export const rules_custom_installed = PositiveInteger; export const rules_not_installed = PositiveInteger; export const rules_not_updated = PositiveInteger; +export const note = t.string; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts index 9b311b1b58ea7..2ba9ec7f83253 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts @@ -105,3 +105,4 @@ export const updated_by = Joi.string(); export const version = Joi.number() .integer() .min(1); +export const note = Joi.string(); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts index c7899f3afa7b8..e37abf3746ae6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts @@ -1243,4 +1243,101 @@ describe('create rules schema', () => { 'child "severity" fails because ["severity" must be one of [low, medium, high, critical]]' ); }); + + describe('note', () => { + test('You can set note to a string', () => { + expect( + updateRulesSchema.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: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + note: '# some documentation title', + }).error + ).toBeFalsy(); + }); + + test('You can set note to an empty string', () => { + expect( + updateRulesSchema.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: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + note: '', + }).error + ).toBeFalsy(); + }); + + // Note: If you're looking to remove `note`, omit `note` entirely + test('You cannot set note to null', () => { + expect( + updateRulesSchema.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: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + note: null, + }).error.message + ).toEqual('child "note" fails because ["note" must be a string]'); + }); + + test('You cannot set note as an object', () => { + expect( + updateRulesSchema.validate & { note: object }>>({ + 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: 'low', + interval: '5m', + type: 'query', + references: ['index-1'], + query: 'some query', + language: 'kuery', + max_signals: 1, + note: { + somethingMadeUp: { somethingElse: true }, + }, + }).error.message + ).toEqual('child "note" fails because ["note" must be a string]'); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts index 3e5a608d6b657..a72105142d287 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts @@ -33,6 +33,7 @@ import { threat, references, id, + note, version, } from './schemas'; /* eslint-enable @typescript-eslint/camelcase */ @@ -76,5 +77,6 @@ export const updateRulesSchema = Joi.object({ type: type.required(), threat: threat.default([]), references: references.default([]), + note: note.allow(''), version, }).xor('id', 'rule_id'); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts index c8205859407c0..ea87950a59b78 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts @@ -37,6 +37,7 @@ export const createRules = ({ to, type, references, + note, version, }: CreateRuleParams): Promise => { return alertsClient.create({ @@ -67,6 +68,7 @@ export const createRules = ({ to, type, references, + note, version, }, schedule: { interval }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts index 33f60bf0ba543..39b596dfed855 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts @@ -21,7 +21,7 @@ describe('getExportAll', () => { const exports = await getExportAll(alertsClient); expect(exports).toEqual({ rulesNdjson: - '{"created_at":"2019-12-13T16:40:33.400Z","updated_at":"2019-12-13T16:40:33.400Z","created_by":"elastic","description":"Detecting root and admin users","enabled":true,"false_positives":[],"filters":[{"query":{"match_phrase":{"host.name":"some-host"}}}],"from":"now-6m","id":"04128c15-0d1b-4716-a4c5-46997ac7f3bd","immutable":false,"index":["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"interval":"5m","rule_id":"rule-1","language":"kuery","output_index":".siem-signals","max_signals":100,"risk_score":50,"name":"Detect Root/Admin Users","query":"user.name: root or user.name: admin","references":["http://www.example.com","https://ww.example.com"],"timeline_id":"some-timeline-id","timeline_title":"some-timeline-title","meta":{"someMeta":"someField"},"severity":"high","updated_by":"elastic","tags":[],"to":"now","type":"query","threat":[{"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/"}]}],"version":1}\n', + '{"created_at":"2019-12-13T16:40:33.400Z","updated_at":"2019-12-13T16:40:33.400Z","created_by":"elastic","description":"Detecting root and admin users","enabled":true,"false_positives":[],"filters":[{"query":{"match_phrase":{"host.name":"some-host"}}}],"from":"now-6m","id":"04128c15-0d1b-4716-a4c5-46997ac7f3bd","immutable":false,"index":["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"interval":"5m","rule_id":"rule-1","language":"kuery","output_index":".siem-signals","max_signals":100,"risk_score":50,"name":"Detect Root/Admin Users","query":"user.name: root or user.name: admin","references":["http://www.example.com","https://ww.example.com"],"timeline_id":"some-timeline-id","timeline_title":"some-timeline-title","meta":{"someMeta":"someField"},"severity":"high","updated_by":"elastic","tags":[],"to":"now","type":"query","threat":[{"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/"}]}],"note":"# Investigative notes","version":1}\n', exportDetails: '{"exported_count":1,"missing_rules":[],"missing_rules_count":0}\n', }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts index 83b487163bdfb..1406c7c9000b2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts @@ -29,7 +29,7 @@ describe('get_export_by_object_ids', () => { const exports = await getExportByObjectIds(alertsClient, objects); expect(exports).toEqual({ rulesNdjson: - '{"created_at":"2019-12-13T16:40:33.400Z","updated_at":"2019-12-13T16:40:33.400Z","created_by":"elastic","description":"Detecting root and admin users","enabled":true,"false_positives":[],"filters":[{"query":{"match_phrase":{"host.name":"some-host"}}}],"from":"now-6m","id":"04128c15-0d1b-4716-a4c5-46997ac7f3bd","immutable":false,"index":["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"interval":"5m","rule_id":"rule-1","language":"kuery","output_index":".siem-signals","max_signals":100,"risk_score":50,"name":"Detect Root/Admin Users","query":"user.name: root or user.name: admin","references":["http://www.example.com","https://ww.example.com"],"timeline_id":"some-timeline-id","timeline_title":"some-timeline-title","meta":{"someMeta":"someField"},"severity":"high","updated_by":"elastic","tags":[],"to":"now","type":"query","threat":[{"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/"}]}],"version":1}\n', + '{"created_at":"2019-12-13T16:40:33.400Z","updated_at":"2019-12-13T16:40:33.400Z","created_by":"elastic","description":"Detecting root and admin users","enabled":true,"false_positives":[],"filters":[{"query":{"match_phrase":{"host.name":"some-host"}}}],"from":"now-6m","id":"04128c15-0d1b-4716-a4c5-46997ac7f3bd","immutable":false,"index":["auditbeat-*","filebeat-*","packetbeat-*","winlogbeat-*"],"interval":"5m","rule_id":"rule-1","language":"kuery","output_index":".siem-signals","max_signals":100,"risk_score":50,"name":"Detect Root/Admin Users","query":"user.name: root or user.name: admin","references":["http://www.example.com","https://ww.example.com"],"timeline_id":"some-timeline-id","timeline_title":"some-timeline-title","meta":{"someMeta":"someField"},"severity":"high","updated_by":"elastic","tags":[],"to":"now","type":"query","threat":[{"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/"}]}],"note":"# Investigative notes","version":1}\n', exportDetails: '{"exported_count":1,"missing_rules":[],"missing_rules_count":0}\n', }); }); @@ -117,6 +117,7 @@ describe('get_export_by_object_ids', () => { ], }, ], + note: '# Investigative notes', version: 1, }, ], diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts index 3d9ec128963f6..3b5ef57d3dcb6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -42,6 +42,7 @@ export const installPrepackagedRules = ( type, threat, references, + note, version, } = rule; return [ @@ -74,6 +75,7 @@ export const installPrepackagedRules = ( type, threat, references, + note, version, }), ]; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts index 5fdef59a72f04..628f4033d5665 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts @@ -42,6 +42,7 @@ export const patchRules = async ({ to, type, references, + note, version, throttle, }: PatchRuleParams): Promise => { @@ -75,6 +76,7 @@ export const patchRules = async ({ references, version, throttle, + note, }); const nextParams = defaults( @@ -102,6 +104,7 @@ export const patchRules = async ({ to, type, references, + note, version: calculatedVersion, } ); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts index 7889267a7267b..1051ac28885b8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts @@ -42,6 +42,7 @@ export const updatePrepackagedRules = async ( references, version, throttle, + note, } = rule; // Note: we do not pass down enabled as we do not want to suddenly disable @@ -75,6 +76,7 @@ export const updatePrepackagedRules = async ( references, version, throttle, + note, }); }); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts index 3a10841b70d7e..3987654589bdd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts @@ -43,6 +43,7 @@ export const updateRules = async ({ references, version, throttle, + note, }: UpdateRuleParams): Promise => { const rule = await readRules({ alertsClient, ruleId, id }); if (rule == null) { @@ -74,6 +75,7 @@ export const updateRules = async ({ references, version, throttle, + note, }); const update = await alertsClient.update({ @@ -106,6 +108,7 @@ export const updateRules = async ({ to, type, references, + note, version: calculatedVersion, }, }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/patches/update_note.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/patches/update_note.json new file mode 100644 index 0000000000000..4262cc63008a1 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/patches/update_note.json @@ -0,0 +1,4 @@ +{ + "rule_id": "query-with-note", + "note": " # Changes only the note to this new value" +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json index 082dd5205a142..286aa6c771d15 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json @@ -78,5 +78,6 @@ ], "timeline_id": "timeline_id", "timeline_title": "timeline_title", + "note": "# note markdown", "version": 1 } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_note.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_note.json new file mode 100644 index 0000000000000..71e6ce2f83040 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_note.json @@ -0,0 +1,10 @@ +{ + "name": "Query with a note", + "description": "Query with a note", + "rule_id": "query-with-note", + "risk_score": 1, + "severity": "high", + "type": "query", + "query": "user.name: root or user.name: admin", + "note": "# investigative note markdown header" +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_note.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_note.json new file mode 100644 index 0000000000000..de850906b2859 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_note.json @@ -0,0 +1,10 @@ +{ + "name": "Query with a note", + "description": "Query with a note", + "rule_id": "query-with-note", + "risk_score": 1, + "severity": "high", + "type": "query", + "query": "user.name: root or user.name: admin", + "note": "# Changes only note to this new value on update" +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts index fded0696ff8bf..922651edc4082 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -28,6 +28,7 @@ export const sampleRuleAlertParams = ( references: ['http://google.com'], riskScore: riskScore ? riskScore : 50, maxSignals: maxSignals ? maxSignals : 10000, + note: '', filters: undefined, savedId: undefined, timelineId: undefined, @@ -340,6 +341,7 @@ export const sampleRule = (): Partial => { tags: ['some fake tag 1', 'some fake tag 2'], to: 'now', type: 'query', + note: '', }; }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts index b71a7080f4147..30dac114ac506 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts @@ -79,6 +79,7 @@ describe('buildBulkBody', () => { tags: ['some fake tag 1', 'some fake tag 2'], type: 'query', to: 'now', + note: '', enabled: true, created_by: 'elastic', updated_by: 'elastic', @@ -168,6 +169,7 @@ describe('buildBulkBody', () => { tags: ['some fake tag 1', 'some fake tag 2'], type: 'query', to: 'now', + note: '', enabled: true, created_by: 'elastic', updated_by: 'elastic', @@ -255,6 +257,7 @@ describe('buildBulkBody', () => { tags: ['some fake tag 1', 'some fake tag 2'], type: 'query', to: 'now', + note: '', enabled: true, created_by: 'elastic', updated_by: 'elastic', @@ -335,6 +338,7 @@ describe('buildBulkBody', () => { tags: ['some fake tag 1', 'some fake tag 2'], type: 'query', to: 'now', + note: '', enabled: true, created_by: 'elastic', updated_by: 'elastic', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.test.ts index af0883f4ce6b5..c2900782ed676 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.test.ts @@ -60,6 +60,7 @@ describe('buildRule', () => { tags: ['some fake tag 1', 'some fake tag 2'], to: 'now', type: 'query', + note: '', updated_by: 'elastic', updated_at: rule.updated_at, created_at: rule.created_at, @@ -116,6 +117,7 @@ describe('buildRule', () => { tags: ['some fake tag 1', 'some fake tag 2'], to: 'now', type: 'query', + note: '', updated_by: 'elastic', version: 1, updated_at: rule.updated_at, @@ -161,6 +163,7 @@ describe('buildRule', () => { tags: ['some fake tag 1', 'some fake tag 2'], to: 'now', type: 'query', + note: '', updated_by: 'elastic', version: 1, updated_at: rule.updated_at, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.ts index 70465bf1d9201..9baf6a55b7f48 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.ts @@ -44,6 +44,7 @@ export const buildRule = ({ risk_score: ruleParams.riskScore, output_index: ruleParams.outputIndex, description: ruleParams.description, + note: ruleParams.note, from: ruleParams.from, immutable: ruleParams.immutable, index: ruleParams.index, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts index 93e9c7f6e0d50..0a50c33fbbfe4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts @@ -66,6 +66,7 @@ describe('buildSignal', () => { tags: ['some fake tag 1', 'some fake tag 2'], to: 'now', type: 'query', + note: '', updated_at: signal.rule.updated_at, created_at: signal.rule.created_at, }, @@ -131,6 +132,7 @@ describe('buildSignal', () => { tags: ['some fake tag 1', 'some fake tag 2'], to: 'now', type: 'query', + note: '', updated_at: signal.rule.updated_at, created_at: signal.rule.created_at, }, @@ -202,6 +204,7 @@ describe('buildSignal', () => { tags: ['some fake tag 1', 'some fake tag 2'], to: 'now', type: 'query', + note: '', updated_at: signal.rule.updated_at, created_at: signal.rule.created_at, }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts index f7fabfb980195..b467dfdaff305 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -55,6 +55,7 @@ export const signalRulesAlertType = ({ validate: { params: schema.object({ description: schema.string(), + note: schema.nullable(schema.string()), falsePositives: schema.arrayOf(schema.string(), { defaultValue: [] }), from: schema.string(), ruleId: schema.string(), diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts index 5e5ff157c92c6..fa43ac1debb92 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts @@ -24,6 +24,7 @@ export interface ThreatParams { export interface RuleAlertParams { description: string; + note: string | undefined | null; enabled: boolean; falsePositives: string[]; filters: PartialFilter[] | undefined | null; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/utils.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/utils.ts index bef700d0409a5..1570124cdb92b 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/utils.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/utils.ts @@ -285,6 +285,7 @@ export const getComplexRule = (ruleId = 'rule-1'): Partial ], timeline_id: 'timeline_id', timeline_title: 'timeline_title', + note: '# some investigation documentation', version: 1, query: 'user.name: root or user.name: admin', }); @@ -370,6 +371,7 @@ export const getComplexRuleOutput = (ruleId = 'rule-1'): Partial