Skip to content

Commit

Permalink
[SIEM][Detection Engine] Adds signal to ECS event.kind and fixes stat…
Browse files Browse the repository at this point in the history
…us in signals (elastic#51772)

## Summary

* Adds signal to the ECS event.kind when it copies over a signal
* Creates a `original_event` if needed within signal so additional look ups don't have to happen
* Fixes a bug with `signal.status` where it was not plumbed correctly
* Adds extra unit tests around the filter
* Adds missing unit tests around utils I didn't add before
* Fixes a typing issue with output of a signal

Example signal output:

Original event turns into this:
```ts
  "event" : {
    "dataset" : "socket",
    "kind" : "signal",
    "action" : "existing_socket",
    "id" : "ffec6797-b92f-4436-bb40-69bac2c21874",
    "module" : "system"
  },
```

Signal amplification turns into this where it contains original_event looks like this:

```ts
  "signal" : {
    "parent" : {
      "id" : "xNRlqW4BHe9nqdOi2358",
      "type" : "event",
      "index" : "auditbeat",
      "depth" : 1
    },
    "original_time" : "2019-11-26T20:27:11.169Z",
    "status" : "open",
    "rule" : {
      "id" : "643fbd2f-a3c9-449e-ba95-e3d89000a72a",
      "rule_id" : "rule-1",
      "false_positives" : [ ],
      "max_signals" : 100,
      "risk_score" : 1,
      "description" : "Detecting root and admin users",
      "from" : "now-6m",
      "immutable" : false,
      "interval" : "5m",
      "language" : "kuery",
      "name" : "Detect Root/Admin Users",
      "query" : "user.name: root or user.name: admin",
      "references" : [
        "http://www.example.com",
        "https://ww.example.com"
      ],
      "severity" : "high",
      "tags" : [ ],
      "type" : "query",
      "to" : "now",
      "enabled" : true,
      "created_by" : "elastic_some_user",
      "updated_by" : "elastic_some_user"
    },
    "original_event" : {
      "dataset" : "socket",
      "kind" : "state",
      "action" : "existing_socket",
      "id" : "ffec6797-b92f-4436-bb40-69bac2c21874",
      "module" : "system"
    }
  }
```
### Checklist

Use ~~strikethroughs~~ to remove checklist items you don't feel are applicable to this PR.

~~- [ ] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)~~

~~- [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)~~

~~- [ ] [Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~~

- [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

~~- [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~~

### For maintainers

~~- [ ] This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~~

- [x] This includes a feature addition or change that requires a release note and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)
  • Loading branch information
FrankHassanabad committed Nov 26, 2019
1 parent a812ee9 commit 3b78aaf
Show file tree
Hide file tree
Showing 7 changed files with 750 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { SignalSourceHit, SignalSearchResponse, RuleTypeParams } from '../types';
import {
SignalSourceHit,
SignalSearchResponse,
RuleTypeParams,
OutputRuleAlertRest,
} from '../types';

export const sampleRuleAlertParams = (
maxSignals: number | undefined,
maxSignals?: number | undefined,
riskScore?: number | undefined
): RuleTypeParams => ({
ruleId: 'rule-1',
Expand All @@ -32,7 +37,7 @@ export const sampleRuleAlertParams = (
meta: undefined,
});

export const sampleDocNoSortId = (someUuid: string): SignalSourceHit => ({
export const sampleDocNoSortId = (someUuid: string = sampleIdGuid): SignalSourceHit => ({
_index: 'myFakeSignalIndex',
_type: 'doc',
_score: 100,
Expand All @@ -44,7 +49,7 @@ export const sampleDocNoSortId = (someUuid: string): SignalSourceHit => ({
},
});

export const sampleDocNoSortIdNoVersion = (someUuid: string): SignalSourceHit => ({
export const sampleDocNoSortIdNoVersion = (someUuid: string = sampleIdGuid): SignalSourceHit => ({
_index: 'myFakeSignalIndex',
_type: 'doc',
_score: 100,
Expand All @@ -55,7 +60,7 @@ export const sampleDocNoSortIdNoVersion = (someUuid: string): SignalSourceHit =>
},
});

export const sampleDocWithSortId = (someUuid: string): SignalSourceHit => ({
export const sampleDocWithSortId = (someUuid: string = sampleIdGuid): SignalSourceHit => ({
_index: 'myFakeSignalIndex',
_type: 'doc',
_score: 100,
Expand Down Expand Up @@ -138,7 +143,9 @@ export const sampleBulkCreateDuplicateResult = {
],
};

export const sampleDocSearchResultsNoSortId = (someUuid: string): SignalSearchResponse => ({
export const sampleDocSearchResultsNoSortId = (
someUuid: string = sampleIdGuid
): SignalSearchResponse => ({
took: 10,
timed_out: false,
_shards: {
Expand All @@ -159,7 +166,7 @@ export const sampleDocSearchResultsNoSortId = (someUuid: string): SignalSearchRe
});

export const sampleDocSearchResultsNoSortIdNoVersion = (
someUuid: string
someUuid: string = sampleIdGuid
): SignalSearchResponse => ({
took: 10,
timed_out: false,
Expand All @@ -180,7 +187,9 @@ export const sampleDocSearchResultsNoSortIdNoVersion = (
},
});

export const sampleDocSearchResultsNoSortIdNoHits = (someUuid: string): SignalSearchResponse => ({
export const sampleDocSearchResultsNoSortIdNoHits = (
someUuid: string = sampleIdGuid
): SignalSearchResponse => ({
took: 10,
timed_out: false,
_shards: {
Expand Down Expand Up @@ -222,7 +231,9 @@ export const repeatedSearchResultsWithSortId = (
},
});

export const sampleDocSearchResultsWithSortId = (someUuid: string): SignalSearchResponse => ({
export const sampleDocSearchResultsWithSortId = (
someUuid: string = sampleIdGuid
): SignalSearchResponse => ({
took: 10,
timed_out: false,
_shards: {
Expand All @@ -243,3 +254,31 @@ export const sampleDocSearchResultsWithSortId = (someUuid: string): SignalSearch
});

export const sampleRuleGuid = '04128c15-0d1b-4716-a4c5-46997ac7f3bd';
export const sampleIdGuid = 'e1e08ddc-5e37-49ff-a258-5393aa44435a';

export const sampleRule = (): Partial<OutputRuleAlertRest> => {
return {
created_by: 'elastic',
description: 'Detecting root and admin users',
enabled: true,
false_positives: [],
from: 'now-6m',
id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
immutable: false,
index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],
interval: '5m',
risk_score: 50,
rule_id: 'rule-1',
language: 'kuery',
max_signals: 100,
name: 'Detect Root/Admin Users',
output_index: '.siem-signals',
query: 'user.name: root or user.name: admin',
references: ['http://www.example.com', 'https://ww.example.com'],
severity: 'high',
updated_by: 'elastic',
tags: [],
to: 'now',
type: 'query',
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { getQueryFilter, getFilter } from './get_filter';
import { savedObjectsClientMock } from 'src/core/server/mocks';
import { AlertServices } from '../../../../../alerting/server/types';
import { PartialFilter } from './types';

describe('get_filter', () => {
let savedObjectsClient = savedObjectsClientMock.create();
Expand Down Expand Up @@ -145,6 +146,103 @@ describe('get_filter', () => {
});
});

test('it should work with a simple filter as a kuery without meta information', () => {
const esQuery = getQueryFilter(
'host.name: windows',
'kuery',
[
{
query: {
match_phrase: {
'host.name': 'siem-windows',
},
},
},
],
['auditbeat-*']
);
expect(esQuery).toEqual({
bool: {
must: [],
filter: [
{
bool: {
should: [
{
match: {
'host.name': 'windows',
},
},
],
minimum_should_match: 1,
},
},
{
match_phrase: {
'host.name': 'siem-windows',
},
},
],
should: [],
must_not: [],
},
});
});

test('it should work with a simple filter as a kuery without meta information with an exists', () => {
const query: PartialFilter = {
query: {
match_phrase: {
'host.name': 'siem-windows',
},
},
} as PartialFilter;

const exists: PartialFilter = {
exists: {
field: 'host.hostname',
},
} as PartialFilter;

const esQuery = getQueryFilter(
'host.name: windows',
'kuery',
[query, exists],
['auditbeat-*']
);
expect(esQuery).toEqual({
bool: {
must: [],
filter: [
{
bool: {
should: [
{
match: {
'host.name': 'windows',
},
},
],
minimum_should_match: 1,
},
},
{
match_phrase: {
'host.name': 'siem-windows',
},
},
{
exists: {
field: 'host.hostname',
},
},
],
should: [],
must_not: [],
},
});
});

test('it should work with a simple filter that is disabled as a kuery', () => {
const esQuery = getQueryFilter(
'host.name: windows',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,6 @@ export type OutputRuleAlertRest = RuleAlertParamsRest & {
updated_by: string | undefined | null;
};

export type OutputRuleES = OutputRuleAlertRest & {
status: 'open' | 'closed';
};

export type UpdateRuleAlertParamsRest = Partial<RuleAlertParamsRest> & {
id: string | undefined;
rule_id: RuleAlertParams['ruleId'] | undefined;
Expand Down
Loading

0 comments on commit 3b78aaf

Please sign in to comment.