Skip to content

Commit

Permalink
[Security Solution][Detections] Fixes Severity Override not matching …
Browse files Browse the repository at this point in the history
…for Elastic Endpoint Security rule (elastic#74317)

## Summary

Fixes an issue where the `Severity Override` would not match for the `Elastic Endpoint Security` rule. This is a temporary fix until we can provide more robust comparisons between the user provided `severityMapping.value` (string) and the `severityMapping.field`'s type.  

### Checklist

- [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios
  • Loading branch information
spong committed Aug 5, 2020
1 parent 98f02e7 commit 7345e50
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,24 @@ export const sampleDocNoSortId = (
sort: [],
});

export const sampleDocSeverity = (
severity?: Array<string | number | null> | string | number | null
): SignalSourceHit => ({
_index: 'myFakeSignalIndex',
_type: 'doc',
_score: 100,
_version: 1,
_id: sampleIdGuid,
_source: {
someKey: 'someValue',
'@timestamp': '2020-04-20T21:27:45+0000',
event: {
severity: severity ?? 100,
},
},
sort: [],
});

export const sampleEmptyDocSearchResults = (): SignalSearchResponse => ({
took: 10,
timed_out: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { sampleDocNoSortId } from '../__mocks__/es_results';
import { sampleDocNoSortId, sampleDocSeverity } from '../__mocks__/es_results';
import { buildSeverityFromMapping } from './build_severity_from_mapping';

describe('buildSeverityFromMapping', () => {
beforeEach(() => {
jest.clearAllMocks();
});

test('severity defaults to provided if mapping is incomplete', () => {
test('severity defaults to provided if mapping is undefined', () => {
const severity = buildSeverityFromMapping({
doc: sampleDocNoSortId(),
severity: 'low',
Expand All @@ -22,5 +22,45 @@ describe('buildSeverityFromMapping', () => {
expect(severity).toEqual({ severity: 'low', severityMeta: {} });
});

test('severity is overridden to highest matched mapping', () => {
const severity = buildSeverityFromMapping({
doc: sampleDocSeverity(23),
severity: 'low',
severityMapping: [
{ field: 'event.severity', operator: 'equals', value: '23', severity: 'critical' },
{ field: 'event.severity', operator: 'equals', value: '23', severity: 'low' },
{ field: 'event.severity', operator: 'equals', value: '11', severity: 'critical' },
{ field: 'event.severity', operator: 'equals', value: '23', severity: 'medium' },
],
});

expect(severity).toEqual({
severity: 'critical',
severityMeta: {
severityOverrideField: 'event.severity',
},
});
});

test('severity is overridden when field is event.severity and source value is number', () => {
const severity = buildSeverityFromMapping({
doc: sampleDocSeverity(23),
severity: 'low',
severityMapping: [
{ field: 'event.severity', operator: 'equals', value: '13', severity: 'low' },
{ field: 'event.severity', operator: 'equals', value: '23', severity: 'medium' },
{ field: 'event.severity', operator: 'equals', value: '33', severity: 'high' },
{ field: 'event.severity', operator: 'equals', value: '43', severity: 'critical' },
],
});

expect(severity).toEqual({
severity: 'medium',
severityMeta: {
severityOverrideField: 'event.severity',
},
});
});

// TODO: Enhance...
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,38 @@ interface BuildSeverityFromMappingReturn {
severityMeta: Meta; // TODO: Stricter types
}

const severitySortMapping = {
low: 0,
medium: 1,
high: 2,
critical: 3,
};

export const buildSeverityFromMapping = ({
doc,
severity,
severityMapping,
}: BuildSeverityFromMappingProps): BuildSeverityFromMappingReturn => {
if (severityMapping != null && severityMapping.length > 0) {
let severityMatch: SeverityMappingItem | undefined;
severityMapping.forEach((mapping) => {
// TODO: Expand by verifying fieldType from index via doc._index
const mappedValue = get(mapping.field, doc._source);
if (mapping.value === mappedValue) {

// Sort the SeverityMapping from low to high, so last match (highest severity) is used
const severityMappingSorted = severityMapping.sort(
(a, b) => severitySortMapping[a.severity] - severitySortMapping[b.severity]
);

severityMappingSorted.forEach((mapping) => {
const docValue = get(mapping.field, doc._source);
// TODO: Expand by verifying fieldType from index via doc._index
// Till then, explicit parsing of event.severity (long) to number. If not ECS, this could be
// another datatype, but until we can lookup datatype we must assume number for the Elastic
// Endpoint Security rule to function correctly
let parsedMappingValue: string | number = mapping.value;
if (mapping.field === 'event.severity') {
parsedMappingValue = Math.floor(Number(parsedMappingValue));
}

if (parsedMappingValue === docValue) {
severityMatch = { ...mapping };
}
});
Expand Down

0 comments on commit 7345e50

Please sign in to comment.