diff --git a/x-pack/plugins/security_solution/public/resolver/data_access_layer/factory.ts b/x-pack/plugins/security_solution/public/resolver/data_access_layer/factory.ts index 55d52d4ba3252..7b09e748c0c28 100644 --- a/x-pack/plugins/security_solution/public/resolver/data_access_layer/factory.ts +++ b/x-pack/plugins/security_solution/public/resolver/data_access_layer/factory.ts @@ -11,6 +11,8 @@ import { ResolverRelatedEvents, ResolverTree, ResolverEntityIndex, + ResolverPaginatedEvents, + SafeResolverEvent, } from '../../../common/endpoint/types'; /** @@ -22,12 +24,54 @@ export function dataAccessLayerFactory( const dataAccessLayer: DataAccessLayer = { /** * Used to get non-process related events for a node. + * @deprecated use the new API (eventsWithEntityIDAndCategory & event) instead */ async relatedEvents(entityID: string): Promise { - return context.services.http.post(`/api/endpoint/resolver/${entityID}/events`, { - query: { events: 100 }, + const response: ResolverPaginatedEvents = await context.services.http.post( + '/api/endpoint/resolver/events', + { + query: {}, + body: JSON.stringify({ + filter: `process.entity_id:"${entityID}" and not event.category:"process"`, + }), + } + ); + + return { ...response, entityID }; + }, + + /** + * Return events that have `process.entity_id` that includes `entityID` and that have + * a `event.category` that includes `category`. + */ + async eventsWithEntityIDAndCategory( + entityID: string, + category: string, + after?: string + ): Promise { + return context.services.http.post('/api/endpoint/resolver/events', { + query: { afterEvent: after }, + body: JSON.stringify({ + filter: `process.entity_id:"${entityID}" and event.category:"${category}"`, + }), }); }, + + /** + * Return up to one event that has an `event.id` that includes `eventID`. + */ + async event(eventID: string): Promise { + const response: ResolverPaginatedEvents = await context.services.http.post( + '/api/endpoint/resolver/events', + { + query: {}, + body: JSON.stringify({ filter: `event.id:"${eventID}"` }), + } + ); + const [oneEvent] = response.events; + return oneEvent ?? null; + }, + /** * Used to get descendant and ancestor process events for a node. */ diff --git a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/emptify_mock.ts b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/emptify_mock.ts index 631eab18fc014..88a3052a61f74 100644 --- a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/emptify_mock.ts +++ b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/emptify_mock.ts @@ -44,6 +44,7 @@ export function emptifyMock( return { metadata, dataAccessLayer: { + ...dataAccessLayer, /** * Fetch related events for an entity ID */ diff --git a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children.ts b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children.ts index fd086bd9b984e..09625e5726b1d 100644 --- a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children.ts +++ b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children.ts @@ -6,6 +6,7 @@ import { ResolverRelatedEvents, + SafeResolverEvent, ResolverTree, ResolverEntityIndex, } from '../../../../common/endpoint/types'; @@ -58,6 +59,29 @@ export function noAncestorsTwoChildren(): { dataAccessLayer: DataAccessLayer; me }); }, + /** + * Return events that have `process.entity_id` that includes `entityID` and that have + * a `event.category` that includes `category`. + */ + async eventsWithEntityIDAndCategory( + entityID: string, + category: string, + after?: string + ): Promise<{ + events: SafeResolverEvent[]; + nextEvent: string | null; + }> { + const events: SafeResolverEvent[] = []; + return { + events, + nextEvent: null, + }; + }, + + async event(_eventID: string): Promise { + return null; + }, + /** * Fetch a ResolverTree for a entityID */ diff --git a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_in_index_called_awesome_index.ts b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_in_index_called_awesome_index.ts index 86450b25eb1da..3bbe4bcf51060 100644 --- a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_in_index_called_awesome_index.ts +++ b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_in_index_called_awesome_index.ts @@ -8,6 +8,7 @@ import { ResolverRelatedEvents, ResolverTree, ResolverEntityIndex, + SafeResolverEvent, } from '../../../../common/endpoint/types'; import { mockEndpointEvent } from '../../mocks/endpoint_event'; import { mockTreeWithNoAncestorsAnd2Children } from '../../mocks/resolver_tree'; @@ -69,6 +70,32 @@ export function noAncestorsTwoChildenInIndexCalledAwesomeIndex(): { }); }, + async eventsWithEntityIDAndCategory( + entityID: string, + category, + after?: string + ): Promise<{ + events: SafeResolverEvent[]; + nextEvent: string | null; + }> { + return { + events: [ + mockEndpointEvent({ + entityID, + eventCategory: category, + }), + ], + nextEvent: null, + }; + }, + + async event(eventID: string): Promise { + return mockEndpointEvent({ + entityID: metadata.entityIDs.origin, + eventID, + }); + }, + /** * Fetch a ResolverTree for a entityID */ diff --git a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_with_related_events_on_origin.ts b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_with_related_events_on_origin.ts index ec773a09ae8e0..6fb84eaf7fda6 100644 --- a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_with_related_events_on_origin.ts +++ b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/no_ancestors_two_children_with_related_events_on_origin.ts @@ -10,7 +10,9 @@ import { ResolverRelatedEvents, ResolverTree, ResolverEntityIndex, + SafeResolverEvent, } from '../../../../common/endpoint/types'; +import * as eventModel from '../../../../common/endpoint/models/event'; interface Metadata { /** @@ -56,31 +58,62 @@ export function noAncestorsTwoChildrenWithRelatedEventsOnOrigin(): { /** * Fetch related events for an entity ID */ - relatedEvents(entityID: string): Promise { + async relatedEvents(entityID: string): Promise { /** * Respond with the mocked related events when the origin's related events are fetched. **/ const events = entityID === metadata.entityIDs.origin ? tree.relatedEvents.events : []; - return Promise.resolve({ + return { entityID, events, nextEvent: null, - }); + }; + }, + + /** + * Any of the origin's related events by category. + * `entityID` must match the origin node's `process.entity_id`. + * Does not respect the `_after` parameter. + */ + async eventsWithEntityIDAndCategory( + entityID: string, + category: string, + after?: string + ): Promise<{ events: SafeResolverEvent[]; nextEvent: string | null }> { + const events = + entityID === metadata.entityIDs.origin + ? tree.relatedEvents.events.filter((event) => + eventModel.eventCategory(event).includes(category) + ) + : []; + return { + events, + nextEvent: null, + }; + }, + + /** + * Any of the origin's related events by event.id + */ + async event(eventID: string): Promise { + return ( + tree.relatedEvents.events.find((event) => eventModel.eventID(event) === eventID) ?? null + ); }, /** * Fetch a ResolverTree for a entityID */ - resolverTree(): Promise { - return Promise.resolve(tree); + async resolverTree(): Promise { + return tree; }, /** * Get entities matching a document. */ - entities(): Promise { - return Promise.resolve([{ entity_id: metadata.entityIDs.origin }]); + async entities(): Promise { + return [{ entity_id: metadata.entityIDs.origin }]; }, }, }; diff --git a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/pausify_mock.ts b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/pausify_mock.ts index 6a4955b104b8f..a3ec667385470 100644 --- a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/pausify_mock.ts +++ b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/pausify_mock.ts @@ -89,6 +89,7 @@ export function pausifyMock({ } }, dataAccessLayer: { + ...dataAccessLayer, /** * Fetch related events for an entity ID */ diff --git a/x-pack/plugins/security_solution/public/resolver/types.ts b/x-pack/plugins/security_solution/public/resolver/types.ts index 4dc614abe3345..64147dd8feb75 100644 --- a/x-pack/plugins/security_solution/public/resolver/types.ts +++ b/x-pack/plugins/security_solution/public/resolver/types.ts @@ -16,6 +16,7 @@ import { ResolverTree, ResolverEntityIndex, SafeResolverEvent, + ResolverPaginatedEvents, } from '../../common/endpoint/types'; /** @@ -503,6 +504,21 @@ export interface DataAccessLayer { */ relatedEvents: (entityID: string) => Promise; + /** + * Return events that have `process.entity_id` that includes `entityID` and that have + * a `event.category` that includes `category`. + */ + eventsWithEntityIDAndCategory: ( + entityID: string, + category: string, + after?: string + ) => Promise; + + /** + * Return up to one event that has an `event.id` that includes `eventID`. + */ + event: (eventID: string) => Promise; + /** * Fetch a ResolverTree for a entityID */