Skip to content

Commit

Permalink
[Security Solution][Detections] Modify threshold rule synthetic signa…
Browse files Browse the repository at this point in the history
…l generation to use data from last hit in bucket (#82444)

* Fix threshold rule synthetic signal generation

* Use top_hits aggregation

* Add timestampOverride

* Account for when threshold.field is not supplied

* Ensure we're getting the last event when threshold.field is not provided

* Add missing import
  • Loading branch information
madirey authored Nov 11, 2020
1 parent 5ab41f5 commit f4126ea
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { TimestampOverrideOrUndefined } from '../../../../common/detection_engine/schemas/common/schemas';
import {
SortOrderOrUndefined,
TimestampOverrideOrUndefined,
} from '../../../../common/detection_engine/schemas/common/schemas';

interface BuildEventsSearchQuery {
aggregations?: unknown;
Expand All @@ -13,6 +16,7 @@ interface BuildEventsSearchQuery {
to: string;
filter: unknown;
size: number;
sortOrder?: SortOrderOrUndefined;
searchAfterSortId: string | number | undefined;
timestampOverride: TimestampOverrideOrUndefined;
}
Expand All @@ -25,6 +29,7 @@ export const buildEventsSearchQuery = ({
filter,
size,
searchAfterSortId,
sortOrder,
timestampOverride,
}: BuildEventsSearchQuery) => {
const timestamp = timestampOverride ?? '@timestamp';
Expand Down Expand Up @@ -108,7 +113,7 @@ export const buildEventsSearchQuery = ({
sort: [
{
[timestamp]: {
order: 'asc',
order: sortOrder ?? 'asc',
},
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,29 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { sampleDocNoSortIdNoVersion } from './__mocks__/es_results';
import { getThresholdSignalQueryFields } from './bulk_create_threshold_signals';

describe('getThresholdSignalQueryFields', () => {
it('should return proper fields for match_phrase filters', () => {
const mockHit = {
...sampleDocNoSortIdNoVersion(),
_source: {
'@timestamp': '2020-11-03T02:31:47.431Z',
event: {
dataset: 'traefik.access',
module: 'traefik',
},
traefik: {
access: {
entryPointName: 'web-secure',
},
},
url: {
domain: 'kibana.siem.estc.dev',
},
},
};
const mockFilters = {
bool: {
must: [],
Expand Down Expand Up @@ -71,15 +90,28 @@ describe('getThresholdSignalQueryFields', () => {
},
};

expect(getThresholdSignalQueryFields(mockFilters)).toEqual({
'event.module': 'traefik',
expect(getThresholdSignalQueryFields(mockHit, mockFilters)).toEqual({
'event.dataset': 'traefik.access',
'event.module': 'traefik',
'traefik.access.entryPointName': 'web-secure',
'url.domain': 'kibana.siem.estc.dev',
});
});

it('should return proper fields object for nested match filters', () => {
const mockHit = {
...sampleDocNoSortIdNoVersion(),
_source: {
'@timestamp': '2020-11-03T02:31:47.431Z',
event: {
dataset: 'traefik.access',
module: 'traefik',
},
url: {
domain: 'kibana.siem.estc.dev',
},
},
};
const filters = {
bool: {
must: [],
Expand All @@ -104,7 +136,7 @@ describe('getThresholdSignalQueryFields', () => {
should: [
{
match: {
'event.dataset': 'traefik.access',
'event.dataset': 'traefik.*',
},
},
],
Expand All @@ -120,13 +152,23 @@ describe('getThresholdSignalQueryFields', () => {
},
};

expect(getThresholdSignalQueryFields(filters)).toEqual({
'event.module': 'traefik',
expect(getThresholdSignalQueryFields(mockHit, filters)).toEqual({
'event.dataset': 'traefik.access',
'event.module': 'traefik',
});
});

it('should return proper object for simple match filters', () => {
const mockHit = {
...sampleDocNoSortIdNoVersion(),
_source: {
'@timestamp': '2020-11-03T02:31:47.431Z',
event: {
dataset: 'traefik.access',
module: 'traefik',
},
},
};
const filters = {
bool: {
must: [],
Expand Down Expand Up @@ -154,13 +196,23 @@ describe('getThresholdSignalQueryFields', () => {
},
};

expect(getThresholdSignalQueryFields(filters)).toEqual({
'event.module': 'traefik',
expect(getThresholdSignalQueryFields(mockHit, filters)).toEqual({
'event.dataset': 'traefik.access',
'event.module': 'traefik',
});
});

it('should return proper object for simple match_phrase filters', () => {
const mockHit = {
...sampleDocNoSortIdNoVersion(),
_source: {
'@timestamp': '2020-11-03T02:31:47.431Z',
event: {
dataset: 'traefik.access',
module: 'traefik',
},
},
};
const filters = {
bool: {
must: [],
Expand Down Expand Up @@ -188,13 +240,22 @@ describe('getThresholdSignalQueryFields', () => {
},
};

expect(getThresholdSignalQueryFields(filters)).toEqual({
expect(getThresholdSignalQueryFields(mockHit, filters)).toEqual({
'event.module': 'traefik',
'event.dataset': 'traefik.access',
});
});

it('should return proper object for exists filters', () => {
const mockHit = {
...sampleDocNoSortIdNoVersion(),
_source: {
'@timestamp': '2020-11-03T02:31:47.431Z',
event: {
module: 'traefik',
},
},
};
const filters = {
bool: {
should: [
Expand Down Expand Up @@ -226,6 +287,46 @@ describe('getThresholdSignalQueryFields', () => {
minimum_should_match: 1,
},
};
expect(getThresholdSignalQueryFields(filters)).toEqual({});
expect(getThresholdSignalQueryFields(mockHit, filters)).toEqual({});
});

it('should NOT add invalid characters from CIDR such as the "/" proper object for simple match_phrase filters', () => {
const mockHit = {
...sampleDocNoSortIdNoVersion(),
_source: {
'@timestamp': '2020-11-03T02:31:47.431Z',
destination: {
ip: '192.168.0.16',
},
event: {
module: 'traefik',
},
},
};
const filters = {
bool: {
must: [],
filter: [
{
bool: {
should: [
{
match: {
'destination.ip': '192.168.0.0/16',
},
},
],
minimum_should_match: 1,
},
},
],
should: [],
must_not: [],
},
};

expect(getThresholdSignalQueryFields(mockHit, filters)).toEqual({
'destination.ip': '192.168.0.16',
});
});
});
Loading

0 comments on commit f4126ea

Please sign in to comment.