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

[Security_Solution][Endpoint] Resolver leverage ancestry array for queries #69264

Merged
merged 24 commits into from
Jun 19, 2020
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
facb497
Adding alerts route
jonathan-buttner Jun 3, 2020
97c359d
Fixing move conflicts
jonathan-buttner Jun 4, 2020
29cb1a0
Merge branch 'master' of github.com:elastic/kibana into resolver-aler…
jonathan-buttner Jun 5, 2020
2e33743
Adding related alerts generator changes, tests, and script updates
jonathan-buttner Jun 5, 2020
ba93305
Merge branch 'master' into resolver-alerts-api
elasticmachine Jun 5, 2020
cb467d7
Merge branch 'master' of github.com:elastic/kibana into resolver-aler…
jonathan-buttner Jun 8, 2020
5031536
Fixing missed parameter
jonathan-buttner Jun 8, 2020
b804ddb
Aligning the AlertEvent and ResolverEvent definition
jonathan-buttner Jun 9, 2020
d63287b
Fixing type errors
jonathan-buttner Jun 10, 2020
db8c3fd
Merge branch 'master' of github.com:elastic/kibana into resolver-aler…
jonathan-buttner Jun 10, 2020
7155add
Fixing import error
jonathan-buttner Jun 10, 2020
89d0df6
Merge branch 'master' of github.com:elastic/kibana into resolver-aler…
jonathan-buttner Jun 15, 2020
e6e6749
Adding ancestry functionality in generator
jonathan-buttner Jun 15, 2020
1050a56
Creating some tests for ancestry field
jonathan-buttner Jun 15, 2020
8f46bea
Making progress on the ancestry
jonathan-buttner Jun 16, 2020
0060c24
Fixing the ancestry verification
jonathan-buttner Jun 16, 2020
db147d8
Fixing existing tests
jonathan-buttner Jun 16, 2020
3f8c433
Fixing master merge conflicts
jonathan-buttner Jun 16, 2020
882b2f9
Removing unused code and fixing test
jonathan-buttner Jun 16, 2020
f8e898f
Adding more comments
jonathan-buttner Jun 16, 2020
e253fed
Fixing endgame queries
jonathan-buttner Jun 18, 2020
b732114
Fixing merge conflicts
jonathan-buttner Jun 18, 2020
0bf79ca
Fixing merge conflicts
jonathan-buttner Jun 18, 2020
78a18f9
Merge branch 'master' into resolver-be-ancestry
elasticmachine Jun 19, 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 @@ -10,6 +10,7 @@ import {
TreeNode,
RelatedEventCategory,
ECSCategory,
ANCESTRY_LIMIT,
} from './generate_data';

interface Node {
Expand Down Expand Up @@ -113,6 +114,7 @@ describe('data generator', () => {
relatedEvents: 0,
relatedAlerts: 0,
});
tree.ancestry.delete(tree.origin.id);
});

it('creates an alert for the origin node but no other nodes', () => {
Expand Down Expand Up @@ -159,6 +161,30 @@ describe('data generator', () => {
return (inRelated || inRelatedAlerts || inLifecycle) && event.process.entity_id === node.id;
};

const verifyAncestry = (event: Event, genTree: Tree) => {
if (event.process.Ext.ancestry.length > 0) {
expect(event.process.parent?.entity_id).toBe(event.process.Ext.ancestry[0]);
}
for (let i = 0; i < event.process.Ext.ancestry.length; i++) {
const ancestor = event.process.Ext.ancestry[i];
const parent = genTree.children.get(ancestor) || genTree.ancestry.get(ancestor);
expect(ancestor).toBe(parent?.lifecycle[0].process.entity_id);

// the next ancestor should be the grandparent
if (i + 1 < event.process.Ext.ancestry.length) {
const grandparent = event.process.Ext.ancestry[i + 1];
expect(grandparent).toBe(parent?.lifecycle[0].process.parent?.entity_id);
}
}
};

it('has ancestry array defined', () => {
expect(tree.origin.lifecycle[0].process.Ext.ancestry.length).toBe(ANCESTRY_LIMIT);
for (const event of tree.allEvents) {
verifyAncestry(event, tree);
}
});

it('has the right related events for each node', () => {
const checkRelatedEvents = (node: TreeNode) => {
expect(node.relatedEvents.length).toEqual(4);
Expand All @@ -185,8 +211,6 @@ describe('data generator', () => {
for (const node of tree.children.values()) {
checkRelatedEvents(node);
}

checkRelatedEvents(tree.origin);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

these changes are because the origin node is now included in the ancestry map.

});

it('has the right number of related alerts for each node', () => {
Expand All @@ -202,7 +226,8 @@ describe('data generator', () => {
});

it('has the right number of ancestors', () => {
expect(tree.ancestry.size).toEqual(ancestors);
// +1 for the origin node
expect(tree.ancestry.size).toEqual(ancestors + 1);
});

it('has the right number of total children', () => {
Expand Down Expand Up @@ -239,10 +264,7 @@ describe('data generator', () => {
const children = tree.children.get(event.process.entity_id);
if (children) {
expect(eventInNode(event, children)).toBeTruthy();
return;
}

expect(eventInNode(event, tree.origin)).toBeTruthy();
});
});

Expand All @@ -260,8 +282,6 @@ describe('data generator', () => {
total += nodeEventCount(node);
}

total += nodeEventCount(tree.origin);

expect(tree.allEvents.length).toEqual(total);
});
});
Expand Down
41 changes: 34 additions & 7 deletions x-pack/plugins/security_solution/common/endpoint/generate_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { factory as policyFactory } from './models/policy_config';

export type Event = AlertEvent | EndpointEvent;
export const ANCESTRY_LIMIT: number = 2;

interface EventOptions {
timestamp?: number;
Expand All @@ -26,6 +27,7 @@ interface EventOptions {
eventType?: string;
eventCategory?: string | string[];
processName?: string;
ancestry?: string[];
}

const Windows: HostOS[] = [
Expand Down Expand Up @@ -238,6 +240,7 @@ export interface Tree {
* Map of entity_id to node
*/
ancestry: Map<string, TreeNode>;
// TODO add the origin to the ancestry array to make test verification easier
origin: TreeNode;
/**
* All events from children, ancestry, origin, and the alert in a single array
Expand Down Expand Up @@ -341,7 +344,9 @@ export class EndpointDocGenerator {
public generateAlert(
ts = new Date().getTime(),
entityID = this.randomString(10),
parentEntityID?: string
parentEntityID?: string,
ancestryArray: string[] = [],
ancestryLimit: number = 2
): AlertEvent {
return {
...this.commonInfo,
Expand Down Expand Up @@ -416,6 +421,9 @@ export class EndpointDocGenerator {
sha1: 'fake sha1',
sha256: 'fake sha256',
},
// simulate a finite ancestry array size, the endpoint limits the ancestry array to 20 entries we'll use
// 2 so that the backend can handle that case
Ext: { ancestry: ancestryArray.slice(0, ANCESTRY_LIMIT) },
},
dll: [
{
Expand Down Expand Up @@ -469,6 +477,9 @@ export class EndpointDocGenerator {
entity_id: options.entityID ? options.entityID : this.randomString(10),
parent: options.parentEntityID ? { entity_id: options.parentEntityID } : undefined,
name: options.processName ? options.processName : randomProcessName(),
// simulate a finite ancestry array size, the endpoint limits the ancestry array to 20 entries we'll use
// 2 so that the backend can handle that case
Ext: { ancestry: options.ancestry?.slice(0, ANCESTRY_LIMIT) || [] },
},
};
}
Expand Down Expand Up @@ -522,9 +533,6 @@ export class EndpointDocGenerator {
throw Error(`could not find origin while building tree: ${alert.process.entity_id}`);
}

// remove the origin node from the ancestry array
ancestryNodes.delete(alert.process.entity_id);

const children = Array.from(
this.descendantsTreeGenerator(
alert,
Expand Down Expand Up @@ -676,7 +684,7 @@ export class EndpointDocGenerator {
}
};

// generate related alerts for rootW
// generate related alerts for root
const processDuration: number = 6 * 3600;
if (this.randomN(100) < pctWithRelated) {
addRelatedEvents(ancestor, processDuration, events);
Expand All @@ -701,6 +709,8 @@ export class EndpointDocGenerator {
ancestor = this.generateEvent({
timestamp,
parentEntityID: ancestor.process.entity_id,
// add the parent to the ancestry array
ancestry: [ancestor.process.entity_id, ...ancestor.process.Ext.ancestry],
});
events.push(ancestor);
timestamp = timestamp + 1000;
Expand All @@ -714,6 +724,7 @@ export class EndpointDocGenerator {
parentEntityID: ancestor.process.parent?.entity_id,
eventCategory: 'process',
eventType: 'end',
ancestry: ancestor.process.Ext.ancestry,
})
);
}
Expand All @@ -732,7 +743,12 @@ export class EndpointDocGenerator {
}
}
events.push(
this.generateAlert(timestamp, ancestor.process.entity_id, ancestor.process.parent?.entity_id)
this.generateAlert(
timestamp,
ancestor.process.entity_id,
ancestor.process.parent?.entity_id,
ancestor.process.Ext.ancestry
)
);
return events;
}
Expand Down Expand Up @@ -787,6 +803,10 @@ export class EndpointDocGenerator {
const child = this.generateEvent({
timestamp,
parentEntityID: currentState.event.process.entity_id,
ancestry: [
currentState.event.process.entity_id,
...currentState.event.process.Ext.ancestry,
],
});

maxChildren = this.randomN(maxChildrenPerNode + 1);
Expand All @@ -808,6 +828,7 @@ export class EndpointDocGenerator {
parentEntityID: child.process.parent?.entity_id,
eventCategory: 'process',
eventType: 'end',
ancestry: child.process.Ext.ancestry,
});
}
if (this.randomN(100) < percentNodesWithRelated) {
Expand Down Expand Up @@ -852,6 +873,7 @@ export class EndpointDocGenerator {
parentEntityID: node.process.parent?.entity_id,
eventCategory: eventInfo.category,
eventType: eventInfo.creationType,
ancestry: node.process.Ext.ancestry,
});
}
}
Expand All @@ -870,7 +892,12 @@ export class EndpointDocGenerator {
) {
for (let i = 0; i < relatedAlerts; i++) {
const ts = node['@timestamp'] + this.randomN(alertCreationTime) * 1000;
yield this.generateAlert(ts, node.process.entity_id, node.process.parent?.entity_id);
yield this.generateAlert(
ts,
node.process.entity_id,
node.process.parent?.entity_id,
node.process.Ext.ancestry
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,10 @@ export function parentEntityId(event: ResolverEvent): string | undefined {
}
return event.process.parent?.entity_id;
}

export function ancestryArray(event: ResolverEvent): string[] {
if (isLegacyEvent(event)) {
return [];
}
return event.process.Ext.ancestry;
}
6 changes: 6 additions & 0 deletions x-pack/plugins/security_solution/common/endpoint/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,9 @@ export interface AlertEvent {
thread?: ThreadFields[];
uptime: number;
user: string;
Ext: {
ancestry: string[];
};
};
file: {
owner: string;
Expand Down Expand Up @@ -445,6 +448,9 @@ export interface EndpointEvent {
entity_id: string;
name?: string;
};
Ext: {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We use a capital here to avoid collisions with ecs core since the ancestry array is a custom extension field.

Copy link
Contributor

Choose a reason for hiding this comment

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

I was wondering what Ext was for, thanks for the clarification

ancestry: string[];
};
};
}

Expand Down
Loading