Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SIEM][Detection Engine] - Update UI to read rule exceptions_list #69939

Closed
wants to merge 56 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
3b19da1
Updates list entry schema, exposes exception list client, updates tests
yctercero Jun 22, 2020
3388918
create new de list schema and unit tests
yctercero Jun 22, 2020
e977f00
updated route unit tests and types to match new list schema
yctercero Jun 23, 2020
55e29c9
updated existing DE exceptions code so it should now work as is with …
yctercero Jun 23, 2020
65e47ca
test and types cleanup
yctercero Jun 23, 2020
a2f6286
cleanup
yctercero Jun 23, 2020
6b728fe
Merge branch 'master' of github.com:yctercero/kibana into de_lists
yctercero Jun 23, 2020
53536f8
update unit test
yctercero Jun 23, 2020
1713e7f
Merge branch 'master' of github.com:yctercero/kibana into de_lists
yctercero Jun 24, 2020
373fe8c
updates per feedback
yctercero Jun 25, 2020
60c3e88
updated ui to accept exceptions list
yctercero Jun 25, 2020
4a9a23f
Merge branch 'master' of github.com:yctercero/kibana into exceptions_ui
yctercero Jun 25, 2020
223869e
update unit test
yctercero Jun 25, 2020
1f1761f
Merge branch 'master' of github.com:yctercero/kibana into exceptions_ui
yctercero Jun 25, 2020
042228a
Merge branch 'master' of github.com:yctercero/kibana into exceptions_ui
yctercero Jul 2, 2020
ff28bac
reverted type changes
yctercero Jul 2, 2020
001be67
updated rule exception list param to also include list type
yctercero Jul 2, 2020
e50e831
Merge branch 'master' of github.com:yctercero/kibana into exceptions_ui
yctercero Jul 2, 2020
81d26fc
[Security Solution] Renames the `Investigate in Resolver` Timeline ac…
andrew-goldstein Jul 2, 2020
4f65a02
types cleanup
yctercero Jul 2, 2020
113962e
Update component templates list to render empty prompt inside of cont…
cjcenizal Jul 2, 2020
5fcf803
Fix saved query modal overlay (#68826)
patrykkopycinski Jul 2, 2020
f5b2800
[kbn/optimizer] only build specified themes (#70389)
Jul 2, 2020
23ea7ac
[Maps] Fix cannot select Solid fill-color when removing fields (#70621)
nreese Jul 2, 2020
20237b8
[EPM] Use higher priority than default templates (#70640)
jonathan-buttner Jul 2, 2020
67c70e7
Add Snapshot Restore README with quick-testing steps. (#70494)
cjcenizal Jul 2, 2020
6c62c68
chore(NA): upgrade to lodash@4 (#69868)
mistic Jul 3, 2020
ebcec3a
[Maps] show vector tile labels on top (#69444)
nreese Jul 3, 2020
21efd23
Fixed adding an extra space character on selecting alert variable in …
YulNaumenko Jul 3, 2020
5226ea2
[Alerting] document requirements for developing new action types (#69…
pmuellr Jul 3, 2020
54348a7
[Ingest Manager] Add ability to sort to agent configs and package con…
jen-huang Jul 3, 2020
97ad58c
[ML] Changing shared module setup function parameters (#70589)
jgowdyelastic Jul 3, 2020
169147b
[Uptime] Prevent duplicate requests on load for index status (#70585)
shahzad31 Jul 3, 2020
f1888cd
[Rum Dashbaord] Rum selected service view (#70579)
shahzad31 Jul 3, 2020
5159635
[Ingest Pipelines] Load from json (#70297)
jloleysens Jul 3, 2020
8bc27ec
[APM] Optimize services overview (#69648)
dgieselaar Jul 3, 2020
bc1599e
[Composable template] Create / Edit wizard (#70220)
sebelga Jul 3, 2020
d1e6aa7
[Ingest Manager] Update registry URL to point to snapshot registry (#…
ruflin Jul 3, 2020
fa2f60e
[Uptime] Use elastic charts donut (#70364)
shahzad31 Jul 3, 2020
a916e0a
[Lens] Add ability to set colors for y-axis series (#70311)
mbondyra Jul 3, 2020
571a610
Handle timeouts on creating templates (#70635)
Jul 3, 2020
72b3004
[Ingest Manager] Improve agent unenrollment with unenroll action (#70…
nchaulet Jul 3, 2020
e70fcc7
[Telemetry] Add documentation about Application Usage (#70624)
afharo Jul 3, 2020
bbda3f9
[Lens] Fitting functions (#69820)
flash1293 Jul 3, 2020
f3573f3
[Logs UI] Logs overview queries for the observability dashboard (#70413)
Jul 3, 2020
e1da6a1
Add googlecloud metricbeat module to Kibana Home (#70652)
kaiyan-sheng Jul 3, 2020
97ca7bf
Update dependency @elastic/charts to v19.7.0 (#69791)
renovate[bot] Jul 3, 2020
7ec48fd
[Logs UI] Reorganise log rate anomaly table (#69516)
Kerry350 Jul 3, 2020
78fc9fb
[SECURITY] Bug fix for topN on draggables (#70450)
XavierM Jul 3, 2020
c3cacba
logout from transform_poweruser user in after method of transform tes…
Jul 3, 2020
fd15268
[functional tests] test url field formatter on dashboard and discover…
dmlemeshko Jul 3, 2020
e429670
[Security Solution][Endpoint] Update to new manifest format (without …
madirey Jul 4, 2020
86672a7
skip flaky suite (#70762)
mistic Jul 5, 2020
c96d9b4
skip flaky suite (#70764)
mistic Jul 5, 2020
5418533
Merge branch 'exceptions_ui' of https://github.com/yctercero/kibana i…
yctercero Jul 6, 2020
372c32f
updated failing tests
yctercero Jul 6, 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
6 changes: 4 additions & 2 deletions x-pack/plugins/lists/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,14 @@ And you can attach exception list items like so:
{
"field": "actingProcess.file.signer",
"operator": "included",
"match": "Elastic, N.V."
"type": "match",
"value": "Elastic, N.V."
},
{
"field": "event.category",
"operator": "included",
"match_any": [
"type": "match_any",
"value": [
"process",
"malware"
]
Expand Down
6 changes: 2 additions & 4 deletions x-pack/plugins/lists/common/constants.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,8 @@ export const EXISTS = 'exists';
export const NESTED = 'nested';
export const ENTRIES: EntriesArray = [
{
entries: [
{ field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' },
],
field: 'some.field',
entries: [{ field: 'nested.field', operator: 'included', type: 'match', value: 'some value' }],
field: 'some.parentField',
type: 'nested',
},
{ field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import { getEntriesArrayMock, getEntryMatchMock, getEntryNestedMock } from './en
// it checks against every item in that union. Since entries consist of 5
// different entry types, it returns 5 of these. To make more readable,
// extracted here.
const returnedSchemaError = `"Array<({| field: string, operator: "excluded" | "included", type: "match", value: string |} | {| field: string, operator: "excluded" | "included", type: "match_any", value: DefaultStringArray |} | {| field: string, operator: "excluded" | "included", type: "list", value: DefaultStringArray |} | {| field: string, operator: "excluded" | "included", type: "exists" |} | {| entries: Array<({| field: string, operator: "excluded" | "included", type: "match", value: string |} | {| field: string, operator: "excluded" | "included", type: "match_any", value: DefaultStringArray |} | {| field: string, operator: "excluded" | "included", type: "list", value: DefaultStringArray |} | {| field: string, operator: "excluded" | "included", type: "exists" |})>, field: string, type: "nested" |})>"`;
const returnedSchemaError =
'"Array<({| field: string, operator: "excluded" | "included", type: "match", value: string |} | {| field: string, operator: "excluded" | "included", type: "match_any", value: DefaultStringArray |} | {| field: string, list: {| id: string, type: "ip" | "keyword" |}, operator: "excluded" | "included", type: "list" |} | {| field: string, operator: "excluded" | "included", type: "exists" |} | {| entries: Array<{| field: string, operator: "excluded" | "included", type: "match", value: string |}>, field: string, type: "nested" |})>"';

describe('default_entries_array', () => {
test('it should validate an empty array', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';

import { foldLeftRight, getPaths } from '../../siem_common_deps';

import { DefaultNamespace } from './default_namespace';

describe('default_namespace', () => {
test('it should validate "single"', () => {
const payload = 'single';
const decoded = DefaultNamespace.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});

test('it should validate "agnostic"', () => {
const payload = 'agnostic';
const decoded = DefaultNamespace.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(payload);
});

test('it defaults to "single" if "undefined"', () => {
const payload = undefined;
const decoded = DefaultNamespace.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual('single');
});

test('it defaults to "single" if "null"', () => {
const payload = null;
const decoded = DefaultNamespace.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual('single');
});

test('it should NOT validate if not "single" or "agnostic"', () => {
const payload = 'something else';
const decoded = DefaultNamespace.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([
`Invalid value "something else" supplied to "DefaultNamespace"`,
]);
expect(message.schema).toEqual({});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import * as t from 'io-ts';
import { Either } from 'fp-ts/lib/Either';

const namespaceType = t.keyof({ agnostic: null, single: null });
export const namespaceType = t.keyof({ agnostic: null, single: null });

type NamespaceType = t.TypeOf<typeof namespaceType>;

Expand Down
6 changes: 4 additions & 2 deletions x-pack/plugins/lists/common/schemas/types/entries.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import {
EXISTS,
FIELD,
LIST,
LIST_ID,
MATCH,
MATCH_ANY,
NESTED,
OPERATOR,
TYPE,
} from '../../constants.mock';

import {
Expand Down Expand Up @@ -40,9 +42,9 @@ export const getEntryMatchAnyMock = (): EntryMatchAny => ({

export const getEntryListMock = (): EntryList => ({
field: FIELD,
list: { id: LIST_ID, type: TYPE },
operator: OPERATOR,
type: LIST,
value: [ENTRY_VALUE],
});

export const getEntryExistsMock = (): EntryExists => ({
Expand All @@ -52,7 +54,7 @@ export const getEntryExistsMock = (): EntryExists => ({
});

export const getEntryNestedMock = (): EntryNested => ({
entries: [getEntryMatchMock(), getEntryExistsMock()],
entries: [getEntryMatchMock(), getEntryMatchMock()],
field: FIELD,
type: NESTED,
});
Expand Down
22 changes: 18 additions & 4 deletions x-pack/plugins/lists/common/schemas/types/entries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,16 +251,16 @@ describe('Entries', () => {
expect(message.schema).toEqual(payload);
});

test('it should not validate when "value" is not string array', () => {
const payload: Omit<EntryList, 'value'> & { value: string } = {
test('it should not validate when "list" is not expected value', () => {
const payload: Omit<EntryList, 'list'> & { list: string } = {
...getEntryListMock(),
value: 'someListId',
list: 'someListId',
};
const decoded = entriesList.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([
'Invalid value "someListId" supplied to "value"',
'Invalid value "someListId" supplied to "list"',
]);
expect(message.schema).toEqual({});
});
Expand Down Expand Up @@ -338,6 +338,20 @@ describe('Entries', () => {
expect(message.schema).toEqual({});
});

test('it should NOT validate when "entries" contains an entry item that is not type "match"', () => {
const payload: Omit<EntryNested, 'entries'> & {
entries: EntryMatchAny[];
} = { ...getEntryNestedMock(), entries: [getEntryMatchAnyMock()] };
const decoded = entriesNested.decode(payload);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([
'Invalid value "match_any" supplied to "entries,type"',
'Invalid value "["some host name"]" supplied to "entries,value"',
]);
expect(message.schema).toEqual({});
});

test('it should strip out extra keys', () => {
const payload: EntryNested & {
extraKey?: string;
Expand Down
6 changes: 3 additions & 3 deletions x-pack/plugins/lists/common/schemas/types/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import * as t from 'io-ts';

import { operator } from '../common/schemas';
import { operator, type } from '../common/schemas';
import { DefaultStringArray } from '../../siem_common_deps';

export const entriesMatch = t.exact(
Expand All @@ -34,9 +34,9 @@ export type EntryMatchAny = t.TypeOf<typeof entriesMatchAny>;
export const entriesList = t.exact(
t.type({
field: t.string,
list: t.exact(t.type({ id: t.string, type })),
operator,
type: t.keyof({ list: null }),
value: DefaultStringArray,
})
);
export type EntryList = t.TypeOf<typeof entriesList>;
Expand All @@ -52,7 +52,7 @@ export type EntryExists = t.TypeOf<typeof entriesExists>;

export const entriesNested = t.exact(
t.type({
entries: t.array(t.union([entriesMatch, entriesMatchAny, entriesList, entriesExists])),
entries: t.array(entriesMatch),
field: t.string,
type: t.keyof({ nested: null }),
})
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/lists/common/schemas/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
*/
export * from './default_comments_array';
export * from './default_entries_array';
export * from './default_namespace';
export * from './comments';
export * from './entries';
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('useExceptionList', () => {
useExceptionList({
filterOptions: { filter: '', tags: [] },
http: mockKibanaHttpService,
lists: [{ id: 'myListId', namespaceType: 'single' }],
lists: [{ id: 'myListId', namespace_type: 'single' }],
onError: onErrorMock,
pagination: {
page: 1,
Expand Down Expand Up @@ -76,7 +76,7 @@ describe('useExceptionList', () => {
useExceptionList({
filterOptions: { filter: '', tags: [] },
http: mockKibanaHttpService,
lists: [{ id: 'myListId', namespaceType: 'single' }],
lists: [{ id: 'myListId', namespace_type: 'single' }],
onError: onErrorMock,
onSuccess: onSuccessMock,
pagination: {
Expand Down Expand Up @@ -131,7 +131,7 @@ describe('useExceptionList', () => {
initialProps: {
filterOptions: { filter: '', tags: [] },
http: mockKibanaHttpService,
lists: [{ id: 'myListId', namespaceType: 'single' }],
lists: [{ id: 'myListId', namespace_type: 'single' }],
onError: onErrorMock,
onSuccess: onSuccessMock,
pagination: {
Expand All @@ -146,7 +146,7 @@ describe('useExceptionList', () => {
rerender({
filterOptions: { filter: '', tags: [] },
http: mockKibanaHttpService,
lists: [{ id: 'newListId', namespaceType: 'single' }],
lists: [{ id: 'newListId', namespace_type: 'single' }],
onError: onErrorMock,
onSuccess: onSuccessMock,
pagination: {
Expand All @@ -173,7 +173,7 @@ describe('useExceptionList', () => {
useExceptionList({
filterOptions: { filter: '', tags: [] },
http: mockKibanaHttpService,
lists: [{ id: 'myListId', namespaceType: 'single' }],
lists: [{ id: 'myListId', namespace_type: 'single' }],
onError: onErrorMock,
pagination: {
page: 1,
Expand Down Expand Up @@ -210,7 +210,7 @@ describe('useExceptionList', () => {
useExceptionList({
filterOptions: { filter: '', tags: [] },
http: mockKibanaHttpService,
lists: [{ id: 'myListId', namespaceType: 'single' }],
lists: [{ id: 'myListId', namespace_type: 'single' }],
onError: onErrorMock,
pagination: {
page: 1,
Expand Down Expand Up @@ -238,7 +238,7 @@ describe('useExceptionList', () => {
useExceptionList({
filterOptions: { filter: '', tags: [] },
http: mockKibanaHttpService,
lists: [{ id: 'myListId', namespaceType: 'single' }],
lists: [{ id: 'myListId', namespace_type: 'single' }],
onError: onErrorMock,
pagination: {
page: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,25 @@ export const useExceptionList = ({
let exceptions: ExceptionListItemSchema[] = [];
let exceptionListsReturned: ExceptionList[] = [];

const fetchData = async ({ id, namespaceType }: ExceptionIdentifiers): Promise<void> => {
const fetchData = async ({ id, namespace_type }: ExceptionIdentifiers): Promise<void> => {
try {
setLoading(true);

const {
list_id,
namespace_type,
namespace_type: namespaceType,
...restOfExceptionList
} = await fetchExceptionListById({
http,
id,
namespaceType,
namespaceType: namespace_type,
signal: abortCtrl.signal,
});
const fetchListItemsResult = await fetchExceptionListItemsByListId({
filterOptions,
http,
listId: list_id,
namespaceType: namespace_type,
namespaceType,
pagination,
signal: abortCtrl.signal,
});
Expand Down Expand Up @@ -147,8 +147,8 @@ export const useExceptionList = ({
// TODO: Workaround for now. Once api updated, we can pass in array of lists to fetch
await Promise.all(
lists.map(
({ id, namespaceType }: ExceptionIdentifiers): Promise<void> =>
fetchData({ id, namespaceType })
({ id, namespace_type }: ExceptionIdentifiers): Promise<void> =>
fetchData({ id, namespace_type })
)
);

Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/lists/public/exceptions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export interface UseExceptionListProps {

export interface ExceptionIdentifiers {
id: string;
namespaceType: NamespaceType;
namespace_type: NamespaceType;
Copy link
Contributor

Choose a reason for hiding this comment

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

Was this changed to snake case to match the request body? I would expect it to be namespaceType everywhere except the API call, which sends namespace_type: namespaceType

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea it was. Would I specify that change from namespace_type to namespaceType in the graphql types?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Working to update this and a found bug.

type?: string;
}

Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/lists/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ListPlugin } from './plugin';

// exporting these since its required at top level in siem plugin
export { ListClient } from './services/lists/list_client';
export { ExceptionListClient } from './services/exception_lists/exception_list_client';
export { ListPluginSetup } from './types';

export const config = { schema: ConfigSchema };
Expand Down
10 changes: 10 additions & 0 deletions x-pack/plugins/lists/server/saved_objects/exception_list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ export const exceptionListItemMapping: SavedObjectsType['mappings'] = {
field: {
type: 'keyword',
},
list: {
properties: {
id: {
type: 'keyword',
},
type: {
type: 'keyword',
},
},
},
operator: {
type: 'keyword',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"list_id": "endpoint_list",
"item_id": "endpoint_list_item_lg_val_list",
"_tags": ["endpoint", "process", "malware", "os:windows"],
"tags": ["user added string for a tag", "malware"],
"type": "simple",
"description": "This is a sample exception list item with a large value list included",
"name": "Sample Endpoint Exception List Item with large value list",
"comments": [],
"entries": [
{
"field": "event.module",
"operator": "excluded",
"type": "match_any",
"value": ["zeek"]
},
{
"field": "source.ip",
"operator": "excluded",
"type": "list",
"list": { "id": "list-ip", "type": "ip" }
}
]
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"id": "hand_inserted_item_id",
"list_id": "list-ip",
"value": "127.0.0.1"
"value": "10.4.2.140"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export { EntriesArray, namespaceType } from '../../../lists/common/schemas';
Loading