Skip to content

Commit 7dc33f9

Browse files
author
Robert Austin
authored
[Resolver] UI tests for the panel and bug fix (#74421)
* Change the way the resolver simulator works * refactor resolver tree and data access layer mocks * Fix bug where timestamp and pid sometimes don't show in the node detail view * add a few tests for the panel (not done, but worth committing.)
1 parent 5d9f329 commit 7dc33f9

File tree

13 files changed

+390
-237
lines changed

13 files changed

+390
-237
lines changed

x-pack/plugins/security_solution/common/endpoint/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,15 @@ export interface ResolverRelatedEvents {
182182
nextEvent: string | null;
183183
}
184184

185+
/**
186+
* Safe version of `ResolverRelatedEvents`
187+
*/
188+
export interface SafeResolverRelatedEvents {
189+
entityID: string;
190+
events: SafeResolverEvent[];
191+
nextEvent: string | null;
192+
}
193+
185194
/**
186195
* Response structure for the alerts route.
187196
*/
Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,8 @@ import {
99
ResolverTree,
1010
ResolverEntityIndex,
1111
} from '../../../../common/endpoint/types';
12-
import { mockEndpointEvent } from '../../store/mocks/endpoint_event';
13-
import {
14-
mockTreeWithNoAncestorsAnd2Children,
15-
withRelatedEventsOnOrigin,
16-
} from '../../store/mocks/resolver_tree';
12+
import { mockEndpointEvent } from '../../mocks/endpoint_event';
13+
import { mockTreeWithNoAncestorsAnd2Children } from '../../mocks/resolver_tree';
1714
import { DataAccessLayer } from '../../types';
1815

1916
interface Metadata {
@@ -43,24 +40,11 @@ interface Metadata {
4340
/**
4441
* A simple mock dataAccessLayer possible that returns a tree with 0 ancestors and 2 direct children. 1 related event is returned. The parameter to `entities` is ignored.
4542
*/
46-
export function oneAncestorTwoChildren(
47-
{ withRelatedEvents }: { withRelatedEvents: Iterable<[string, string]> | null } = {
48-
withRelatedEvents: null,
49-
}
50-
): { dataAccessLayer: DataAccessLayer; metadata: Metadata } {
43+
export function noAncestorsTwoChildren(): { dataAccessLayer: DataAccessLayer; metadata: Metadata } {
5144
const metadata: Metadata = {
5245
databaseDocumentID: '_id',
5346
entityIDs: { origin: 'origin', firstChild: 'firstChild', secondChild: 'secondChild' },
5447
};
55-
const baseTree = mockTreeWithNoAncestorsAnd2Children({
56-
originID: metadata.entityIDs.origin,
57-
firstChildID: metadata.entityIDs.firstChild,
58-
secondChildID: metadata.entityIDs.secondChild,
59-
});
60-
const composedTree = withRelatedEvents
61-
? withRelatedEventsOnOrigin(baseTree, withRelatedEvents)
62-
: baseTree;
63-
6448
return {
6549
metadata,
6650
dataAccessLayer: {
@@ -70,17 +54,13 @@ export function oneAncestorTwoChildren(
7054
relatedEvents(entityID: string): Promise<ResolverRelatedEvents> {
7155
return Promise.resolve({
7256
entityID,
73-
events:
74-
/* Respond with the mocked related events when the origin's related events are fetched*/ withRelatedEvents &&
75-
entityID === metadata.entityIDs.origin
76-
? composedTree.relatedEvents.events
77-
: [
78-
mockEndpointEvent({
79-
entityID,
80-
name: 'event',
81-
timestamp: 0,
82-
}),
83-
],
57+
events: [
58+
mockEndpointEvent({
59+
entityID,
60+
name: 'event',
61+
timestamp: 0,
62+
}),
63+
],
8464
nextEvent: null,
8565
});
8666
},
@@ -89,7 +69,13 @@ export function oneAncestorTwoChildren(
8969
* Fetch a ResolverTree for a entityID
9070
*/
9171
resolverTree(): Promise<ResolverTree> {
92-
return Promise.resolve(composedTree);
72+
return Promise.resolve(
73+
mockTreeWithNoAncestorsAnd2Children({
74+
originID: metadata.entityIDs.origin,
75+
firstChildID: metadata.entityIDs.firstChild,
76+
secondChildID: metadata.entityIDs.secondChild,
77+
})
78+
);
9379
},
9480

9581
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { DataAccessLayer } from '../../types';
8+
import { mockTreeWithNoAncestorsAndTwoChildrenAndRelatedEventsOnOrigin } from '../../mocks/resolver_tree';
9+
import {
10+
ResolverRelatedEvents,
11+
ResolverTree,
12+
ResolverEntityIndex,
13+
} from '../../../../common/endpoint/types';
14+
15+
interface Metadata {
16+
/**
17+
* The `_id` of the document being analyzed.
18+
*/
19+
databaseDocumentID: string;
20+
/**
21+
* A record of entityIDs to be used in tests assertions.
22+
*/
23+
entityIDs: {
24+
/**
25+
* The entityID of the node related to the document being analyzed.
26+
*/
27+
origin: 'origin';
28+
/**
29+
* The entityID of the first child of the origin.
30+
*/
31+
firstChild: 'firstChild';
32+
/**
33+
* The entityID of the second child of the origin.
34+
*/
35+
secondChild: 'secondChild';
36+
};
37+
}
38+
39+
export function noAncestorsTwoChildrenWithRelatedEventsOnOrigin(): {
40+
dataAccessLayer: DataAccessLayer;
41+
metadata: Metadata;
42+
} {
43+
const metadata: Metadata = {
44+
databaseDocumentID: '_id',
45+
entityIDs: { origin: 'origin', firstChild: 'firstChild', secondChild: 'secondChild' },
46+
};
47+
const tree = mockTreeWithNoAncestorsAndTwoChildrenAndRelatedEventsOnOrigin({
48+
originID: metadata.entityIDs.origin,
49+
firstChildID: metadata.entityIDs.firstChild,
50+
secondChildID: metadata.entityIDs.secondChild,
51+
});
52+
53+
return {
54+
metadata,
55+
dataAccessLayer: {
56+
/**
57+
* Fetch related events for an entity ID
58+
*/
59+
relatedEvents(entityID: string): Promise<ResolverRelatedEvents> {
60+
/**
61+
* Respond with the mocked related events when the origin's related events are fetched.
62+
**/
63+
const events = entityID === metadata.entityIDs.origin ? tree.relatedEvents.events : [];
64+
65+
return Promise.resolve({
66+
entityID,
67+
events,
68+
nextEvent: null,
69+
} as ResolverRelatedEvents);
70+
},
71+
72+
/**
73+
* Fetch a ResolverTree for a entityID
74+
*/
75+
resolverTree(): Promise<ResolverTree> {
76+
return Promise.resolve(tree);
77+
},
78+
79+
/**
80+
* Get an array of index patterns that contain events.
81+
*/
82+
indexPatterns(): string[] {
83+
return ['index pattern'];
84+
},
85+
86+
/**
87+
* Get entities matching a document.
88+
*/
89+
entities(): Promise<ResolverEntityIndex> {
90+
return Promise.resolve([{ entity_id: metadata.entityIDs.origin }]);
91+
},
92+
},
93+
};
94+
}

x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts renamed to x-pack/plugins/security_solution/public/resolver/mocks/endpoint_event.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import { EndpointEvent } from '../../../../common/endpoint/types';
7+
import { EndpointEvent } from '../../../common/endpoint/types';
88

99
/**
1010
* Simple mock endpoint event that works for tree layouts.
@@ -28,10 +28,29 @@ export function mockEndpointEvent({
2828
type: lifecycleType ? lifecycleType : 'start',
2929
category: 'process',
3030
},
31+
agent: {
32+
id: 'agent.id',
33+
version: 'agent.version',
34+
type: 'agent.type',
35+
},
36+
ecs: {
37+
version: 'ecs.version',
38+
},
39+
user: {
40+
name: 'user.name',
41+
domain: 'user.domain',
42+
},
3143
process: {
3244
entity_id: entityID,
45+
executable: 'executable',
46+
args: 'args',
3347
name,
48+
pid: 0,
49+
hash: {
50+
md5: 'hash.md5',
51+
},
3452
parent: {
53+
pid: 0,
3554
entity_id: parentEntityId,
3655
},
3756
},

x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts renamed to x-pack/plugins/security_solution/public/resolver/mocks/resolver_tree.ts

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
*/
66

77
import { mockEndpointEvent } from './endpoint_event';
8-
import { mockRelatedEvent } from './related_event';
9-
import { ResolverTree, ResolverEvent } from '../../../../common/endpoint/types';
8+
import { ResolverTree, ResolverEvent, SafeResolverEvent } from '../../../common/endpoint/types';
109

1110
export function mockTreeWith2AncestorsAndNoChildren({
1211
originID,
@@ -125,11 +124,11 @@ type RelatedEventType = string;
125124
* @param treeToAddRelatedEventsTo the ResolverTree to modify
126125
* @param relatedEventsToAddByCategoryAndType Iterable of `[category, type]` pairs describing related events. e.g. [['dns','info'],['registry','access']]
127126
*/
128-
export function withRelatedEventsOnOrigin(
127+
function withRelatedEventsOnOrigin(
129128
treeToAddRelatedEventsTo: ResolverTree,
130129
relatedEventsToAddByCategoryAndType: Iterable<[RelatedEventCategory, RelatedEventType]>
131130
): ResolverTree {
132-
const events = [];
131+
const events: SafeResolverEvent[] = [];
133132
const byCategory: Record<string, number> = {};
134133
const stats = {
135134
totalAlerts: 0,
@@ -139,14 +138,18 @@ export function withRelatedEventsOnOrigin(
139138
},
140139
};
141140
for (const [category, type] of relatedEventsToAddByCategoryAndType) {
142-
events.push(
143-
mockRelatedEvent({
144-
entityID: treeToAddRelatedEventsTo.entityID,
145-
timestamp: 1,
146-
category,
141+
events.push({
142+
'@timestamp': 1,
143+
event: {
144+
kind: 'event',
147145
type,
148-
})
149-
);
146+
category,
147+
id: 'xyz',
148+
},
149+
process: {
150+
entity_id: treeToAddRelatedEventsTo.entityID,
151+
},
152+
});
150153
stats.events.total++;
151154
stats.events.byCategory[category] = stats.events.byCategory[category]
152155
? stats.events.byCategory[category] + 1
@@ -156,7 +159,7 @@ export function withRelatedEventsOnOrigin(
156159
...treeToAddRelatedEventsTo,
157160
stats,
158161
relatedEvents: {
159-
events,
162+
events: events as ResolverEvent[],
160163
nextEvent: null,
161164
},
162165
};
@@ -309,3 +312,24 @@ export function mockTreeWithNoProcessEvents(): ResolverTree {
309312
},
310313
};
311314
}
315+
316+
export function mockTreeWithNoAncestorsAndTwoChildrenAndRelatedEventsOnOrigin({
317+
originID,
318+
firstChildID,
319+
secondChildID,
320+
}: {
321+
originID: string;
322+
firstChildID: string;
323+
secondChildID: string;
324+
}) {
325+
const baseTree = mockTreeWithNoAncestorsAnd2Children({
326+
originID,
327+
firstChildID,
328+
secondChildID,
329+
});
330+
const withRelatedEvents: Array<[string, string]> = [
331+
['registry', 'access'],
332+
['registry', 'access'],
333+
];
334+
return withRelatedEventsOnOrigin(baseTree, withRelatedEvents);
335+
}

x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
mockTreeWith1AncestorAnd2ChildrenAndAllNodesHave2GraphableEvents,
1616
mockTreeWithAllProcessesTerminated,
1717
mockTreeWithNoProcessEvents,
18-
} from '../mocks/resolver_tree';
18+
} from '../../mocks/resolver_tree';
1919
import { uniquePidForProcess } from '../../models/process_event';
2020
import { EndpointEvent } from '../../../../common/endpoint/types';
2121

x-pack/plugins/security_solution/public/resolver/store/mocks/related_event.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import * as selectors from './selectors';
1212
import {
1313
mockTreeWith2AncestorsAndNoChildren,
1414
mockTreeWithNoAncestorsAnd2Children,
15-
} from './mocks/resolver_tree';
15+
} from '../mocks/resolver_tree';
1616
import { SafeResolverEvent } from '../../../common/endpoint/types';
1717

1818
describe('resolver selectors', () => {

0 commit comments

Comments
 (0)