From 153063c9a13537d4b970ebe6b98dffec72c871f1 Mon Sep 17 00:00:00 2001 From: Georgii Gorbachev Date: Thu, 26 Nov 2020 20:01:29 +0100 Subject: [PATCH] Add FTR integration tests --- .../tests/generating_signals.ts | 153 ++++++++++++++++++ .../signals/severity_risk_overrides/data.json | 55 +++++++ .../severity_risk_overrides/mappings.json | 26 +++ 3 files changed, 234 insertions(+) create mode 100644 x-pack/test/functional/es_archives/signals/severity_risk_overrides/data.json create mode 100644 x-pack/test/functional/es_archives/signals/severity_risk_overrides/mappings.json diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts index 0db3013503a33f..9442d911c3fd95 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts @@ -5,6 +5,7 @@ */ import expect from '@kbn/expect'; +import { orderBy } from 'lodash'; import { EqlCreateSchema, @@ -617,5 +618,157 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + + /** + * Here we test the functionality of Severity and Risk Score overrides (also called "mappings" + * in the code). If the rule specifies a mapping, then the final Severity or Risk Score + * value of the signal will be taken from the mapped field of the source event. + */ + describe('Signals generated from events with custom severity and risk score fields', () => { + beforeEach(async () => { + await esArchiver.load('signals/severity_risk_overrides'); + }); + + afterEach(async () => { + await esArchiver.unload('signals/severity_risk_overrides'); + }); + + const executeRuleAndGetSignals = async (rule: QueryCreateSchema) => { + const { id } = await createRule(supertest, rule); + await waitForRuleSuccess(supertest, id); + await waitForSignalsToBePresent(supertest, 4, [id]); + const signalsResponse = await getSignalsByIds(supertest, [id]); + const signals = signalsResponse.hits.hits.map((hit) => hit._source); + const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc'); + return signalsOrderedByEventId; + }; + + it('should get default severity and risk score if there is no mapping', async () => { + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['signal_overrides']), + severity: 'medium', + risk_score: 75, + }; + + const signals = await executeRuleAndGetSignals(rule); + + expect(signals.length).equal(4); + signals.forEach((s) => { + expect(s.signal.rule.severity).equal('medium'); + expect(s.signal.rule.severity_mapping).eql([]); + + expect(s.signal.rule.risk_score).equal(75); + expect(s.signal.rule.risk_score_mapping).eql([]); + }); + }); + + it('should get overridden severity if the rule has a mapping for it', async () => { + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['signal_overrides']), + severity: 'medium', + severity_mapping: [ + { field: 'my_severity', operator: 'equals', value: 'sev_900', severity: 'high' }, + { field: 'my_severity', operator: 'equals', value: 'sev_max', severity: 'critical' }, + ], + risk_score: 75, + }; + + const signals = await executeRuleAndGetSignals(rule); + const severities = signals.map((s) => ({ + id: s.signal.parent?.id, + value: s.signal.rule.severity, + })); + + expect(signals.length).equal(4); + expect(severities).eql([ + { id: '1', value: 'high' }, + { id: '2', value: 'critical' }, + { id: '3', value: 'critical' }, + { id: '4', value: 'critical' }, + ]); + + signals.forEach((s) => { + expect(s.signal.rule.risk_score).equal(75); + expect(s.signal.rule.risk_score_mapping).eql([]); + expect(s.signal.rule.severity_mapping).eql([ + { field: 'my_severity', operator: 'equals', value: 'sev_900', severity: 'high' }, + { field: 'my_severity', operator: 'equals', value: 'sev_max', severity: 'critical' }, + ]); + }); + }); + + it('should get overridden risk score if the rule has a mapping for it', async () => { + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['signal_overrides']), + severity: 'medium', + risk_score: 75, + risk_score_mapping: [ + { field: 'my_risk', operator: 'equals', value: '', risk_score: undefined }, + ], + }; + + const signals = await executeRuleAndGetSignals(rule); + const riskScores = signals.map((s) => ({ + id: s.signal.parent?.id, + value: s.signal.rule.risk_score, + })); + + expect(signals.length).equal(4); + expect(riskScores).eql([ + { id: '1', value: 31.14 }, + { id: '2', value: 32.14 }, + { id: '3', value: 33.14 }, + { id: '4', value: 34.14 }, + ]); + + signals.forEach((s) => { + expect(s.signal.rule.severity).equal('medium'); + expect(s.signal.rule.severity_mapping).eql([]); + expect(s.signal.rule.risk_score_mapping).eql([ + { field: 'my_risk', operator: 'equals', value: '' }, + ]); + }); + }); + + it('should get overridden severity and risk score if the rule has both mappings', async () => { + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['signal_overrides']), + severity: 'medium', + severity_mapping: [ + { field: 'my_severity', operator: 'equals', value: 'sev_900', severity: 'high' }, + { field: 'my_severity', operator: 'equals', value: 'sev_max', severity: 'critical' }, + ], + risk_score: 75, + risk_score_mapping: [ + { field: 'my_risk', operator: 'equals', value: '', risk_score: undefined }, + ], + }; + + const signals = await executeRuleAndGetSignals(rule); + const values = signals.map((s) => ({ + id: s.signal.parent?.id, + severity: s.signal.rule.severity, + risk: s.signal.rule.risk_score, + })); + + expect(signals.length).equal(4); + expect(values).eql([ + { id: '1', severity: 'high', risk: 31.14 }, + { id: '2', severity: 'critical', risk: 32.14 }, + { id: '3', severity: 'critical', risk: 33.14 }, + { id: '4', severity: 'critical', risk: 34.14 }, + ]); + + signals.forEach((s) => { + expect(s.signal.rule.severity_mapping).eql([ + { field: 'my_severity', operator: 'equals', value: 'sev_900', severity: 'high' }, + { field: 'my_severity', operator: 'equals', value: 'sev_max', severity: 'critical' }, + ]); + expect(s.signal.rule.risk_score_mapping).eql([ + { field: 'my_risk', operator: 'equals', value: '' }, + ]); + }); + }); + }); }); }; diff --git a/x-pack/test/functional/es_archives/signals/severity_risk_overrides/data.json b/x-pack/test/functional/es_archives/signals/severity_risk_overrides/data.json new file mode 100644 index 00000000000000..1f541dc1ef0a57 --- /dev/null +++ b/x-pack/test/functional/es_archives/signals/severity_risk_overrides/data.json @@ -0,0 +1,55 @@ +{ + "type": "doc", + "value": { + "id": "1", + "index": "signal_overrides", + "source": { + "@timestamp": "2020-11-24T13:00:01.000Z", + "my_severity" : "sev_900", + "my_risk": 31.14 + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "2", + "index": "signal_overrides", + "source": { + "@timestamp": "2020-11-24T13:00:02.000Z", + "my_severity": ["sev_900", "sev_max"], + "my_risk": [32.14] + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "3", + "index": "signal_overrides", + "source": { + "@timestamp": "2020-11-24T13:00:03.000Z", + "my_severity": ["sev_max", "sev_900"], + "my_risk": "33.14" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "id": "4", + "index": "signal_overrides", + "source": { + "@timestamp": "2020-11-24T13:00:04.000Z", + "my_severity": "sev_max", + "my_risk": [3.14, "34.14"] + }, + "type": "_doc" + } +} diff --git a/x-pack/test/functional/es_archives/signals/severity_risk_overrides/mappings.json b/x-pack/test/functional/es_archives/signals/severity_risk_overrides/mappings.json new file mode 100644 index 00000000000000..8a67be50e05fe5 --- /dev/null +++ b/x-pack/test/functional/es_archives/signals/severity_risk_overrides/mappings.json @@ -0,0 +1,26 @@ +{ + "type": "index", + "value": { + "index": "signal_overrides", + "mappings": { + "dynamic": "strict", + "properties": { + "@timestamp": { + "type": "date" + }, + "my_severity": { + "type": "keyword" + }, + "my_risk": { + "type": "integer" + } + } + }, + "settings": { + "index": { + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +}