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

[SecuritySolution][Resolver] Adding siem index and guarding process ancestry #71570

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -120,7 +120,7 @@ describe('data generator', () => {

it('creates all events with an empty ancestry array', () => {
for (const event of tree.allEvents) {
expect(event.process.Ext.ancestry.length).toEqual(0);
expect(event.process.Ext!.ancestry!.length).toEqual(0);
}
});
});
Expand Down Expand Up @@ -188,24 +188,24 @@ describe('data generator', () => {
};

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]);
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];
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];
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);
expect(tree.origin.lifecycle[0].process.Ext!.ancestry!.length).toBe(ANCESTRY_LIMIT);
for (const event of tree.allEvents) {
verifyAncestry(event, tree);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ export class EndpointDocGenerator {
timestamp,
parentEntityID: ancestor.process.entity_id,
// add the parent to the ancestry array
ancestry: [ancestor.process.entity_id, ...(ancestor.process.Ext.ancestry ?? [])],
ancestry: [ancestor.process.entity_id, ...(ancestor.process.Ext?.ancestry ?? [])],
ancestryArrayLimit: opts.ancestryArraySize,
parentPid: ancestor.process.pid,
pid: this.randomN(5000),
Expand All @@ -840,7 +840,7 @@ export class EndpointDocGenerator {
parentEntityID: ancestor.process.parent?.entity_id,
eventCategory: 'process',
eventType: 'end',
ancestry: ancestor.process.Ext.ancestry,
ancestry: ancestor.process.Ext?.ancestry,
ancestryArrayLimit: opts.ancestryArraySize,
})
);
Expand All @@ -864,7 +864,7 @@ export class EndpointDocGenerator {
timestamp,
ancestor.process.entity_id,
ancestor.process.parent?.entity_id,
ancestor.process.Ext.ancestry
ancestor.process.Ext?.ancestry
)
);
return events;
Expand Down Expand Up @@ -914,7 +914,7 @@ export class EndpointDocGenerator {
parentEntityID: currentState.event.process.entity_id,
ancestry: [
currentState.event.process.entity_id,
...(currentState.event.process.Ext.ancestry ?? []),
...(currentState.event.process.Ext?.ancestry ?? []),
],
ancestryArrayLimit: opts.ancestryArraySize,
});
Expand All @@ -938,7 +938,7 @@ export class EndpointDocGenerator {
parentEntityID: child.process.parent?.entity_id,
eventCategory: 'process',
eventType: 'end',
ancestry: child.process.Ext.ancestry,
ancestry: child.process.Ext?.ancestry,
ancestryArrayLimit: opts.ancestryArraySize,
});
}
Expand Down Expand Up @@ -984,7 +984,7 @@ export class EndpointDocGenerator {
parentEntityID: node.process.parent?.entity_id,
eventCategory: eventInfo.category,
eventType: eventInfo.creationType,
ancestry: node.process.Ext.ancestry,
ancestry: node.process.Ext?.ancestry,
});
}
}
Expand All @@ -1007,7 +1007,7 @@ export class EndpointDocGenerator {
ts,
node.process.entity_id,
node.process.parent?.entity_id,
node.process.Ext.ancestry
node.process.Ext?.ancestry
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ export function ancestryArray(event: ResolverEvent): string[] | undefined {
if (isLegacyEvent(event)) {
return undefined;
}
return event.process.Ext.ancestry;
// this is to guard against the endpoint accidentally not sending the ancestry array
// otherwise the request will fail when really we should just try using the parent entity id
return event.process.Ext?.ancestry;
}

export function getAncestryAsArray(event: ResolverEvent | undefined): string[] {
Expand Down
8 changes: 4 additions & 4 deletions x-pack/plugins/security_solution/common/endpoint/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,13 +334,13 @@ export interface AlertEvent {
start: number;
thread?: ThreadFields[];
uptime: number;
Ext: {
Ext?: {
/*
* The array has a special format. The entity_ids towards the beginning of the array are closer ancestors and the
* values towards the end of the array are more distant ancestors (grandparents). Therefore
* ancestry_array[0] == process.parent.entity_id and ancestry_array[1] == process.parent.parent.entity_id
*/
ancestry: string[];
ancestry?: string[];
Copy link
Contributor

Choose a reason for hiding this comment

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

my understanding is that all fields and subfields are optional (and untyped) in ecs. maybe runtime validation would be a better solution for this issue.

code_signature: Array<{
subject_name: string;
trusted: boolean;
Expand Down Expand Up @@ -539,8 +539,8 @@ export interface EndpointEvent {
* values towards the end of the array are more distant ancestors (grandparents). Therefore
* ancestry_array[0] == process.parent.entity_id and ancestry_array[1] == process.parent.parent.entity_id
*/
Ext: {
ancestry: string[];
Ext?: {
ancestry?: string[];
};
};
user?: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export function handleEntities(): RequestHandler<unknown, TypeOf<typeof validate
query: { _id, indices },
} = request;

const siemClient = context.securitySolution!.getAppClient();
const queryIndices = indices;
Copy link
Contributor

Choose a reason for hiding this comment

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

why is this reassignment being done?

// if the alert was promoted by a rule it will exist in the signals index so search there too
queryIndices.push(siemClient.getSignalsIndex());
Copy link
Contributor

Choose a reason for hiding this comment

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

if this route is going to be siem specific, maybe we can rename it?


/**
* A safe type for the response based on the semantics of the query.
* We specify _source, asking for `process.entity_id` and we only
Expand All @@ -43,7 +48,8 @@ export function handleEntities(): RequestHandler<unknown, TypeOf<typeof validate
const queryResponse: ExpectedQueryResponse = await context.core.elasticsearch.legacy.client.callAsCurrentUser(
'search',
{
index: indices,
ignoreUnavailable: true,
index: queryIndices,
body: {
// only return process.entity_id
_source: 'process.entity_id',
Expand Down