Skip to content

Commit

Permalink
Updating tests and api for ancestry array and msearch
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathan-buttner committed Jun 26, 2020
1 parent 1287379 commit 54f1a3f
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 49 deletions.
18 changes: 18 additions & 0 deletions x-pack/plugins/security_solution/common/endpoint/models/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,24 @@ export function ancestryArray(event: ResolverEvent): string[] | undefined {
return event.process.Ext.ancestry;
}

export function getAncestryAsArray(event: ResolverEvent | undefined): string[] {
if (!event) {
return [];
}

const ancestors = ancestryArray(event);
if (ancestors) {
return ancestors;
}

const parentID = parentEntityId(event);
if (parentID) {
return [parentID];
}

return [];
}

/**
* @param event The event to get the category for
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export const validateTree = {
params: schema.object({ id: schema.string() }),
query: schema.object({
children: schema.number({ defaultValue: 10, min: 0, max: 100 }),
generations: schema.number({ defaultValue: 3, min: 0, max: 3 }),
ancestors: schema.number({ defaultValue: 3, min: 0, max: 5 }),
events: schema.number({ defaultValue: 100, min: 0, max: 1000 }),
alerts: schema.number({ defaultValue: 100, min: 0, max: 1000 }),
Expand Down Expand Up @@ -66,7 +65,6 @@ export const validateChildren = {
params: schema.object({ id: schema.string() }),
query: schema.object({
children: schema.number({ defaultValue: 10, min: 1, max: 100 }),
generations: schema.number({ defaultValue: 3, min: 1, max: 3 }),
afterChild: schema.maybe(schema.string()),
legacyEndpointID: schema.maybe(schema.string()),
}),
Expand Down
25 changes: 19 additions & 6 deletions x-pack/plugins/security_solution/common/endpoint/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,18 @@ export interface ResolverNodeStats {
*/
export interface ResolverChildNode extends ResolverLifecycleNode {
/**
* A child node's pagination cursor can be null for a couple reasons:
* 1. At the time of querying it could have no children in ES, in which case it will be marked as
* null because we know it does not have children during this query.
* 2. If the max level was reached we do not know if this node has children or not so we'll mark it as null
* nextChild can have 3 different states:
*
* undefined: This indicates that you should not use this node for additional queries. It does not mean that node does
* not have any more direct children. The node could have more direct children but to determine that, use the
* ResolverChildren node's nextChild.
*
* null: Indicates that we have received all the children of the node. There may be more descendants though.
*
* string: Indicates this is a leaf node and it can be used to continue querying for additional descendants
* using this node's entity_id
*/
nextChild: string | null;
nextChild?: string | null;
}

/**
Expand All @@ -91,7 +97,14 @@ export interface ResolverChildNode extends ResolverLifecycleNode {
export interface ResolverChildren {
childNodes: ResolverChildNode[];
/**
* This is the children cursor for the origin of a tree.
* nextChild can have 2 different states:
*
* null: Indicates that we have received all the descendants that can be retrieved using this node. To retrieve more
* nodes in the tree use a cursor provided in one of the returned children. If no other cursor exists then the tree
* is complete.
*
* string: Indicates this node has more descendants that can be retrieved, pass this cursor in while using this node's
* entity_id for the request.
*/
nextChild: string | null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ export function handleChildren(
return async (context, req, res) => {
const {
params: { id },
query: { children, generations, afterChild, legacyEndpointID: endpointID },
query: { children, afterChild, legacyEndpointID: endpointID },
} = req;
try {
const client = context.core.elasticsearch.legacy.client;
const fetcher = new Fetcher(client, id, eventsIndexPattern, alertsIndexPattern, endpointID);

return res.ok({
body: await fetcher.children(children, generations, afterChild),
body: await fetcher.children(children, afterChild),
});
} catch (err) {
log.warn(err);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export function handleTree(
params: { id },
query: {
children,
generations,
ancestors,
events,
alerts,
Expand All @@ -37,7 +36,7 @@ export function handleTree(
const fetcher = new Fetcher(client, id, eventsIndexPattern, alertsIndexPattern, endpointID);

const [childrenNodes, ancestry, relatedEvents, relatedAlerts] = await Promise.all([
fetcher.children(children, generations, afterChild),
fetcher.children(children, afterChild),
fetcher.ancestors(ancestors),
fetcher.events(events, afterEvent),
fetcher.alerts(alerts, afterAlert),
Expand Down
74 changes: 37 additions & 37 deletions x-pack/test/api_integration/apis/endpoint/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
LegacyEndpointEvent,
ResolverNodeStats,
ResolverRelatedAlerts,
ChildNode,
} from '../../../../plugins/security_solution/common/endpoint/types';
import { parentEntityId } from '../../../../plugins/security_solution/common/endpoint/models/event';
import { FtrProviderContext } from '../../ftr_provider_context';
Expand Down Expand Up @@ -246,6 +247,8 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC
percentWithRelated: 100,
numTrees: 1,
alwaysGenMaxChildrenPerNode: true,
useAncestryArray: true,
ancestryArraySize: 2,
};

describe('Resolver', () => {
Expand Down Expand Up @@ -542,13 +545,10 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC

it('returns multiple levels of child process lifecycle events', async () => {
const { body }: { body: ResolverChildren } = await supertest
.get(
`/api/endpoint/resolver/93802/children?legacyEndpointID=${endpointID}&generations=1`
)
.get(`/api/endpoint/resolver/93802/children?legacyEndpointID=${endpointID}&children=10`)
.expect(200);
expect(body.childNodes.length).to.eql(10);
expect(body.nextChild).to.be(null);
expect(body.childNodes[0].nextChild).to.be(null);
expect(body.childNodes.length).to.eql(8);
expect(body.childNodes[0].lifecycle.length).to.eql(1);
expect(
// for some reason the ts server doesn't think `endgame` exists even though we're using ResolverEvent
Expand Down Expand Up @@ -615,69 +615,69 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC
expect(body.childNodes.length).to.eql(12);
// there will be 4 parents, the origin of the tree, and it's 3 children
verifyChildren(body.childNodes, tree, 4, 3);
expect(body.nextChild).to.eql(null);
});

it('returns a single generation of children', async () => {
// this gets a node should have 3 children which were created in succession so that the timestamps
// are ordered correctly to be retrieved in a single call
const distantChildEntityID = Array.from(tree.childrenLevels[0].values())[0].id;
const { body }: { body: ResolverChildren } = await supertest
.get(`/api/endpoint/resolver/${tree.origin.id}/children?generations=1`)
.get(`/api/endpoint/resolver/${distantChildEntityID}/children?children=3`)
.expect(200);
expect(body.childNodes.length).to.eql(3);
verifyChildren(body.childNodes, tree, 1, 3);
expect(body.nextChild).to.not.eql(null);
});

it('paginates the children of the origin node', async () => {
it('paginates the children', async () => {
// this gets a node should have 3 children which were created in succession so that the timestamps
// are ordered correctly to be retrieved in a single call
const distantChildEntityID = Array.from(tree.childrenLevels[0].values())[0].id;
let { body }: { body: ResolverChildren } = await supertest
.get(`/api/endpoint/resolver/${tree.origin.id}/children?generations=1&children=1`)
.get(`/api/endpoint/resolver/${distantChildEntityID}/children?children=1`)
.expect(200);
expect(body.childNodes.length).to.eql(1);
verifyChildren(body.childNodes, tree, 1, 1);
expect(body.nextChild).to.not.be(null);

({ body } = await supertest
.get(
`/api/endpoint/resolver/${tree.origin.id}/children?generations=1&afterChild=${body.nextChild}`
`/api/endpoint/resolver/${distantChildEntityID}/children?children=2&afterChild=${body.nextChild}`
)
.expect(200));
expect(body.childNodes.length).to.eql(2);
verifyChildren(body.childNodes, tree, 1, 2);
expect(body.childNodes[0].nextChild).to.be(null);
expect(body.childNodes[1].nextChild).to.be(null);
});

it('paginates the children of different nodes', async () => {
let { body }: { body: ResolverChildren } = await supertest
.get(`/api/endpoint/resolver/${tree.origin.id}/children?generations=2&children=2`)
.expect(200);
// it should return 4 nodes total, 2 for each level
expect(body.childNodes.length).to.eql(4);
verifyChildren(body.childNodes, tree, 2);
expect(body.nextChild).to.not.be(null);
expect(body.childNodes[0].nextChild).to.not.be(null);
// the second child will not have any results returned for it so it should not have pagination set (the first)
// request to get it's children should start at the beginning aka not passing any pagination parameter
expect(body.childNodes[1].nextChild).to.be(null);

const firstChild = body.childNodes[0];

// get the 3rd child of the origin of the tree
({ body } = await supertest
.get(
`/api/endpoint/resolver/${tree.origin.id}/children?generations=1&children=10&afterChild=${body.nextChild}`
`/api/endpoint/resolver/${distantChildEntityID}/children?children=2&afterChild=${body.nextChild}`
)
.expect(200));
expect(body.childNodes.length).to.be(1);
verifyChildren(body.childNodes, tree, 1, 1);
expect(body.childNodes[0].nextChild).to.be(null);
expect(body.childNodes.length).to.eql(0);
expect(body.nextChild).to.be(null);
});

it('gets all children in two queries', async () => {
// should get all the children of the origin
let { body }: { body: ResolverChildren } = await supertest
.get(`/api/endpoint/resolver/${tree.origin.id}/children?children=3`)
.expect(200);
expect(body.childNodes.length).to.eql(3);
verifyChildren(body.childNodes, tree);
expect(body.nextChild).to.not.be(null);
const firstNodes = [...body.childNodes];

// get the 1 child of the origin of the tree's last child
({ body } = await supertest
.get(
`/api/endpoint/resolver/${firstChild.entityID}/children?generations=1&children=10&afterChild=${firstChild.nextChild}`
`/api/endpoint/resolver/${tree.origin.id}/children?children=10&afterChild=${body.nextChild}`
)
.expect(200));
expect(body.childNodes.length).to.be(1);
verifyChildren(body.childNodes, tree, 1, 1);
expect(body.childNodes[0].nextChild).to.be(null);
expect(body.childNodes.length).to.eql(9);
// put all the results together and we should have all the children
verifyChildren([...firstNodes, ...body.childNodes], tree, 4, 3);
expect(body.nextChild).to.be(null);
});
});
});
Expand All @@ -703,7 +703,7 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC
it('returns a tree', async () => {
const { body }: { body: ResolverTree } = await supertest
.get(
`/api/endpoint/resolver/${tree.origin.id}?children=100&generations=3&ancestors=5&events=4&alerts=4`
`/api/endpoint/resolver/${tree.origin.id}?children=100&ancestors=5&events=5&alerts=5`
)
.expect(200);

Expand Down

0 comments on commit 54f1a3f

Please sign in to comment.