Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ce14648
Simple type change for Type to reduce multiple touch points when addi…
FrankHassanabad Sep 2, 2020
01c8c88
Merge branch 'master' into make-type-common
FrankHassanabad Sep 2, 2020
b49a549
Fixes the cycle deps issue by breaking out a file from utils
FrankHassanabad Sep 2, 2020
048c2d9
Fixed missing jest.mock call
FrankHassanabad Sep 3, 2020
298806c
Merge branch 'master' into add-threat-match
FrankHassanabad Sep 3, 2020
6af9d85
Initial threat_mapping implementation
FrankHassanabad Sep 14, 2020
83d398e
Merge branch 'master' into add-threat-match
FrankHassanabad Sep 14, 2020
4f6ad76
Broke out the schema into a types file for better organization and te…
FrankHassanabad Sep 14, 2020
84f11a7
More unit tests added
FrankHassanabad Sep 14, 2020
0f5496d
Adds more unit tests
FrankHassanabad Sep 14, 2020
581c663
More unit tests added
FrankHassanabad Sep 14, 2020
2aa838e
More unit tests
FrankHassanabad Sep 15, 2020
fea347c
Adds more unit tests
FrankHassanabad Sep 15, 2020
da7b89c
Fixes unit test
FrankHassanabad Sep 15, 2020
0a51480
Added more unit tests
FrankHassanabad Sep 15, 2020
fbab786
Merge branch 'master' into add-threat-match
FrankHassanabad Sep 15, 2020
b992338
Fixed front end type issues after merge from master
FrankHassanabad Sep 15, 2020
d36e090
Added more tests and made the filters quicker and more resliant again…
FrankHassanabad Sep 16, 2020
f32eb78
Merge branch 'master' into add-threat-match
FrankHassanabad Sep 16, 2020
575eecf
More unit tests
FrankHassanabad Sep 16, 2020
68599a9
Adds more unit tests
FrankHassanabad Sep 16, 2020
604c893
Merge branch 'master' into add-threat-match
FrankHassanabad Sep 16, 2020
f3eaff0
Merge branch 'master' into add-threat-match
FrankHassanabad Sep 16, 2020
404fee3
Merge branch 'master' into add-threat-match
FrankHassanabad Sep 17, 2020
3a97f89
Merge branch 'master' into add-threat-match
FrankHassanabad Sep 17, 2020
cf9502e
Fixed type issues from lastest merge from master
FrankHassanabad Sep 17, 2020
680150a
Merge branch 'master' into add-threat-match
FrankHassanabad Sep 20, 2020
903ff63
Fixes from really really good code review of it.
FrankHassanabad Sep 20, 2020
b43f302
Merge branch 'master' into add-threat-match
FrankHassanabad Sep 20, 2020
7bfb649
Updated from master, merged in and implemented error messages from ot…
FrankHassanabad Sep 20, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ export const type = t.keyof({
query: null,
saved_query: null,
threshold: null,
threat_match: null,
});
export type Type = t.TypeOf<typeof type>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,104 @@ export const getAddPrepackagedRulesSchemaDecodedMock = (): AddPrepackagedRulesSc
exceptions_list: [],
rule_id: 'rule-1',
});

export const getAddPrepackagedThreatMatchRulesSchemaMock = (): AddPrepackagedRulesSchema => ({
description: 'some description',
name: 'Query with a rule id',
query: 'user.name: root or user.name: admin',
severity: 'high',
type: 'threat_match',
risk_score: 55,
language: 'kuery',
rule_id: 'rule-1',
version: 1,
threat_query: '*:*',
threat_index: 'list-index',
threat_mapping: [
{
entries: [
{
field: 'host.name',
value: 'host.name',
type: 'mapping',
},
],
},
],
threat_filters: [
{
bool: {
must: [
{
query_string: {
query: 'host.name: linux',
analyze_wildcard: true,
time_zone: 'Zulu',
},
},
],
filter: [],
should: [],
must_not: [],
},
},
],
});

export const getAddPrepackagedThreatMatchRulesSchemaDecodedMock = (): AddPrepackagedRulesSchemaDecoded => ({
author: [],
description: 'some description',
name: 'Query with a rule id',
query: 'user.name: root or user.name: admin',
severity: 'high',
severity_mapping: [],
type: 'threat_match',
risk_score: 55,
risk_score_mapping: [],
language: 'kuery',
references: [],
actions: [],
enabled: false,
false_positives: [],
from: 'now-6m',
interval: '5m',
max_signals: DEFAULT_MAX_SIGNALS,
tags: [],
to: 'now',
threat: [],
throttle: null,
version: 1,
exceptions_list: [],
rule_id: 'rule-1',
threat_query: '*:*',
threat_index: 'list-index',
threat_mapping: [
{
entries: [
{
field: 'host.name',
value: 'host.name',
type: 'mapping',
},
],
},
],
threat_filters: [
{
bool: {
must: [
{
query_string: {
query: 'host.name: linux',
analyze_wildcard: true,
time_zone: 'Zulu',
},
},
],
filter: [],
should: [],
must_not: [],
},
},
],
});
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ import {
RiskScoreMapping,
SeverityMapping,
} from '../common/schemas';
import {
threat_index,
threat_query,
threat_filters,
threat_mapping,
} from '../types/threat_mapping';

import {
DefaultStringArray,
Expand Down Expand Up @@ -116,6 +122,10 @@ export const addPrepackagedRulesSchema = t.intersection([
references: DefaultStringArray, // defaults to empty array of strings if not set during decode
note, // defaults to "undefined" if not set during decode
exceptions_list: DefaultListArray, // defaults to empty array if not set during decode
threat_filters, // defaults to "undefined" if not set during decode
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: For fields that are arrays, do we want to default them to empty arrays? Or did you choose to default them to "undefined" to be more explicit about like if a rule is not of type "threat_match" these fields should not be there (as opposed to them being there and being [])?

Copy link
Contributor Author

@FrankHassanabad FrankHassanabad Sep 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you answered the question below so I think we're good here 👍

threat_mapping, // defaults to "undefined" if not set during decode
threat_query, // defaults to "undefined" if not set during decode
threat_index, // defaults to "undefined" if not set during decode
})
),
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { left } from 'fp-ts/lib/Either';
import {
getAddPrepackagedRulesSchemaMock,
getAddPrepackagedRulesSchemaDecodedMock,
getAddPrepackagedThreatMatchRulesSchemaMock,
getAddPrepackagedThreatMatchRulesSchemaDecodedMock,
} from './add_prepackaged_rules_schema.mock';
import { DEFAULT_MAX_SIGNALS } from '../../../constants';
import { getListArrayMock } from '../types/lists.mock';
Expand Down Expand Up @@ -1597,4 +1599,16 @@ describe('add prepackaged rules schema', () => {
expect(message.schema).toEqual(expected);
});
});

describe('threat_mapping', () => {
test('You can set a threat query, index, mapping, filters on a pre-packaged rule', () => {
const payload = getAddPrepackagedThreatMatchRulesSchemaMock();
const decoded = addPrepackagedRulesSchema.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
const expected = getAddPrepackagedThreatMatchRulesSchemaDecodedMock();
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(expected);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,103 @@ export const getCreateRulesSchemaDecodedMock = (): CreateRulesSchemaDecoded => (
exceptions_list: [],
rule_id: 'rule-1',
});

export const getCreateThreatMatchRulesSchemaMock = (ruleId = 'rule-1'): CreateRulesSchema => ({
description: 'Detecting root and admin users',
name: 'Query with a rule id',
query: 'user.name: root or user.name: admin',
severity: 'high',
type: 'threat_match',
risk_score: 55,
language: 'kuery',
rule_id: ruleId,
threat_query: '*:*',
threat_index: 'list-index',
threat_mapping: [
{
entries: [
{
field: 'host.name',
value: 'host.name',
type: 'mapping',
},
],
},
],
threat_filters: [
{
bool: {
must: [
{
query_string: {
query: 'host.name: linux',
analyze_wildcard: true,
time_zone: 'Zulu',
},
},
],
filter: [],
should: [],
must_not: [],
},
},
],
});

export const getCreateThreatMatchRulesSchemaDecodedMock = (): CreateRulesSchemaDecoded => ({
author: [],
severity_mapping: [],
risk_score_mapping: [],
description: 'Detecting root and admin users',
name: 'Query with a rule id',
query: 'user.name: root or user.name: admin',
severity: 'high',
type: 'threat_match',
risk_score: 55,
language: 'kuery',
references: [],
actions: [],
enabled: true,
false_positives: [],
from: 'now-6m',
interval: '5m',
max_signals: DEFAULT_MAX_SIGNALS,
tags: [],
to: 'now',
threat: [],
throttle: null,
version: 1,
exceptions_list: [],
rule_id: 'rule-1',
threat_query: '*:*',
threat_index: 'list-index',
threat_mapping: [
{
entries: [
{
field: 'host.name',
value: 'host.name',
type: 'mapping',
},
],
},
],
threat_filters: [
{
bool: {
must: [
{
query_string: {
query: 'host.name: linux',
analyze_wildcard: true,
time_zone: 'Zulu',
},
},
],
filter: [],
should: [],
must_not: [],
},
},
],
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { left } from 'fp-ts/lib/Either';
import {
getCreateRulesSchemaMock,
getCreateRulesSchemaDecodedMock,
getCreateThreatMatchRulesSchemaMock,
getCreateThreatMatchRulesSchemaDecodedMock,
} from './create_rules_schema.mock';
import { DEFAULT_MAX_SIGNALS } from '../../../constants';
import { getListArrayMock } from '../types/lists.mock';
Expand Down Expand Up @@ -1661,4 +1663,16 @@ describe('create rules schema', () => {
expect(message.schema).toEqual(expected);
});
});

describe('threat_mapping', () => {
test('You can set a threat query, index, mapping, filters when creating a rule', () => {
const payload = getCreateThreatMatchRulesSchemaMock();
const decoded = createRulesSchema.decode(payload);
const checked = exactCheck(payload, decoded);
const message = pipe(checked, foldLeftRight);
const expected = getCreateThreatMatchRulesSchemaDecodedMock();
expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(expected);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ import {
RiskScoreMapping,
SeverityMapping,
} from '../common/schemas';
import {
threat_index,
threat_query,
threat_filters,
threat_mapping,
} from '../types/threat_mapping';

import {
DefaultStringArray,
Expand Down Expand Up @@ -112,6 +118,10 @@ export const createRulesSchema = t.intersection([
note, // defaults to "undefined" if not set during decode
version: DefaultVersionNumber, // defaults to 1 if not set during decode
exceptions_list: DefaultListArray, // defaults to empty array if not set during decode
threat_mapping, // defaults to "undefined" if not set during decode
threat_query, // defaults to "undefined" if not set during decode
threat_filters, // defaults to "undefined" if not set during decode
threat_index, // defaults to "undefined" if not set during decode
})
),
]);
Expand Down
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 { getCreateRulesSchemaMock } from './create_rules_schema.mock';
import {
getCreateRulesSchemaMock,
getCreateThreatMatchRulesSchemaMock,
} from './create_rules_schema.mock';
import { CreateRulesSchema } from './create_rules_schema';
import { createRuleValidateTypeDependents } from './create_rules_type_dependents';

Expand Down Expand Up @@ -87,4 +90,39 @@ describe('create_rules_type_dependents', () => {
const errors = createRuleValidateTypeDependents(schema);
expect(errors).toEqual(['"threshold.value" has to be bigger than 0']);
});

test('threat_index, threat_query, and threat_mapping are required when type is "threat_match" and validates with it', () => {
const schema: CreateRulesSchema = {
...getCreateRulesSchemaMock(),
type: 'threat_match',
};
const errors = createRuleValidateTypeDependents(schema);
expect(errors).toEqual([
'when "type" is "threat_match", "threat_index" is required',
'when "type" is "threat_match", "threat_query" is required',
'when "type" is "threat_match", "threat_mapping" is required',
]);
});

test('validates with threat_index, threat_query, and threat_mapping when type is "threat_match"', () => {
const schema = getCreateThreatMatchRulesSchemaMock();
const { threat_filters: threatFilters, ...noThreatFilters } = schema;
const errors = createRuleValidateTypeDependents(noThreatFilters);
expect(errors).toEqual([]);
});

test('does NOT validate when threat_mapping is an empty array', () => {
const schema: CreateRulesSchema = {
...getCreateThreatMatchRulesSchemaMock(),
threat_mapping: [],
};
const errors = createRuleValidateTypeDependents(schema);
expect(errors).toEqual(['threat_mapping" must have at least one element']);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh, I see now here why this value defaults to undefined as opposed to empty array given this requirement here.

});

test('validates with threat_index, threat_query, threat_mapping, and an optional threat_filters, when type is "threat_match"', () => {
const schema = getCreateThreatMatchRulesSchemaMock();
const errors = createRuleValidateTypeDependents(schema);
expect(errors).toEqual([]);
});
});
Loading