[Security Solution] [Resolver] Remove related events api (#79036)
* Removing old related events route * Removing outer describe block Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
bc6ba87330
commit
591585df17
|
@ -23,23 +23,6 @@ export const validateTree = {
|
|||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to validate GET requests for non process events for a specific event.
|
||||
*/
|
||||
export const validateRelatedEvents = {
|
||||
params: schema.object({ id: schema.string({ minLength: 1 }) }),
|
||||
query: schema.object({
|
||||
events: schema.number({ defaultValue: 1000, min: 1, max: 10000 }),
|
||||
afterEvent: schema.maybe(schema.string()),
|
||||
legacyEndpointID: schema.maybe(schema.string({ minLength: 1 })),
|
||||
}),
|
||||
body: schema.nullable(
|
||||
schema.object({
|
||||
filter: schema.maybe(schema.string()),
|
||||
})
|
||||
),
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to validate POST requests for `/resolver/events` api.
|
||||
*/
|
||||
|
|
|
@ -197,7 +197,6 @@ export interface SafeResolverTree {
|
|||
*/
|
||||
entityID: string;
|
||||
children: SafeResolverChildren;
|
||||
relatedEvents: Omit<SafeResolverRelatedEvents, 'entityID'>;
|
||||
relatedAlerts: Omit<ResolverRelatedAlerts, 'entityID'>;
|
||||
ancestry: SafeResolverAncestry;
|
||||
lifecycle: SafeResolverEvent[];
|
||||
|
@ -267,15 +266,6 @@ export interface ResolverRelatedEvents {
|
|||
nextEvent: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe version of `ResolverRelatedEvents`
|
||||
*/
|
||||
export interface SafeResolverRelatedEvents {
|
||||
entityID: string;
|
||||
events: SafeResolverEvent[];
|
||||
nextEvent: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response structure for the events route.
|
||||
* `nextEvent` will be set to null when at the time of querying there were no more results to retrieve from ES.
|
||||
|
|
|
@ -8,14 +8,12 @@ import { IRouter } from 'kibana/server';
|
|||
import { EndpointAppContext } from '../types';
|
||||
import {
|
||||
validateTree,
|
||||
validateRelatedEvents,
|
||||
validateEvents,
|
||||
validateChildren,
|
||||
validateAncestry,
|
||||
validateAlerts,
|
||||
validateEntities,
|
||||
} from '../../../common/endpoint/schema/resolver';
|
||||
import { handleRelatedEvents } from './resolver/related_events';
|
||||
import { handleChildren } from './resolver/children';
|
||||
import { handleAncestry } from './resolver/ancestry';
|
||||
import { handleTree } from './resolver/tree';
|
||||
|
@ -26,17 +24,6 @@ import { handleEvents } from './resolver/events';
|
|||
export function registerResolverRoutes(router: IRouter, endpointAppContext: EndpointAppContext) {
|
||||
const log = endpointAppContext.logFactory.get('resolver');
|
||||
|
||||
// this route will be removed in favor of the one below
|
||||
router.post(
|
||||
{
|
||||
// @deprecated use `/resolver/events` instead
|
||||
path: '/api/endpoint/resolver/{id}/events',
|
||||
validate: validateRelatedEvents,
|
||||
options: { authRequired: true },
|
||||
},
|
||||
handleRelatedEvents(log, endpointAppContext)
|
||||
);
|
||||
|
||||
router.post(
|
||||
{
|
||||
path: '/api/endpoint/resolver/events',
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
/**
|
||||
* @deprecated use the `events.ts` file's query instead
|
||||
*/
|
||||
import { EventsQuery } from './related_events';
|
||||
import { PaginationBuilder } from '../utils/pagination';
|
||||
import { legacyEventIndexPattern } from './legacy_event_index_pattern';
|
||||
|
||||
describe('Events query', () => {
|
||||
it('constructs a legacy multi search query', () => {
|
||||
const query = new EventsQuery(new PaginationBuilder(1), 'index-pattern', 'endpointID');
|
||||
// using any here because otherwise ts complains that it doesn't know what bool and filter are
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const msearch: any = query.buildMSearch('1234');
|
||||
expect(msearch[0].index).toBe(legacyEventIndexPattern);
|
||||
expect(msearch[1].query.bool.filter[0]).toStrictEqual({
|
||||
terms: { 'endgame.unique_pid': ['1234'] },
|
||||
});
|
||||
});
|
||||
|
||||
it('constructs a non-legacy multi search query', () => {
|
||||
const query = new EventsQuery(new PaginationBuilder(1), 'index-pattern');
|
||||
// using any here because otherwise ts complains that it doesn't know what bool and filter are
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const msearch: any = query.buildMSearch(['1234', '5678']);
|
||||
expect(msearch[0].index).toBe('index-pattern');
|
||||
expect(msearch[1].query.bool.filter[0]).toStrictEqual({
|
||||
terms: { 'process.entity_id': ['1234', '5678'] },
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
/**
|
||||
* @deprecated use the `events.ts` file's query instead
|
||||
*/
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
import { esKuery } from '../../../../../../../../src/plugins/data/server';
|
||||
import { SafeResolverEvent } from '../../../../../common/endpoint/types';
|
||||
import { ResolverQuery } from './base';
|
||||
import { PaginationBuilder } from '../utils/pagination';
|
||||
import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common';
|
||||
|
||||
/**
|
||||
* Builds a query for retrieving related events for a node.
|
||||
*/
|
||||
export class EventsQuery extends ResolverQuery<SafeResolverEvent[]> {
|
||||
private readonly kqlQuery: JsonObject[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly pagination: PaginationBuilder,
|
||||
indexPattern: string | string[],
|
||||
endpointID?: string,
|
||||
kql?: string
|
||||
) {
|
||||
super(indexPattern, endpointID);
|
||||
if (kql) {
|
||||
this.kqlQuery.push(esKuery.toElasticsearchQuery(esKuery.fromKueryExpression(kql)));
|
||||
}
|
||||
}
|
||||
|
||||
protected legacyQuery(endpointID: string, uniquePIDs: string[]): JsonObject {
|
||||
return {
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
...this.kqlQuery,
|
||||
{
|
||||
terms: { 'endgame.unique_pid': uniquePIDs },
|
||||
},
|
||||
{
|
||||
term: { 'agent.id': endpointID },
|
||||
},
|
||||
{
|
||||
term: { 'event.kind': 'event' },
|
||||
},
|
||||
{
|
||||
bool: {
|
||||
must_not: {
|
||||
term: { 'event.category': 'process' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
...this.pagination.buildQueryFields('endgame.serial_event_id', 'desc'),
|
||||
};
|
||||
}
|
||||
|
||||
protected query(entityIDs: string[]): JsonObject {
|
||||
return {
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
...this.kqlQuery,
|
||||
{
|
||||
terms: { 'process.entity_id': entityIDs },
|
||||
},
|
||||
{
|
||||
term: { 'event.kind': 'event' },
|
||||
},
|
||||
{
|
||||
bool: {
|
||||
must_not: {
|
||||
term: { 'event.category': 'process' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
...this.pagination.buildQueryFields('event.id', 'desc'),
|
||||
};
|
||||
}
|
||||
|
||||
formatResponse(response: SearchResponse<SafeResolverEvent>): SafeResolverEvent[] {
|
||||
return this.getResults(response);
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @deprecated use the `resolver/events` route and handler instead
|
||||
*/
|
||||
import { TypeOf } from '@kbn/config-schema';
|
||||
import { RequestHandler, Logger } from 'kibana/server';
|
||||
import { eventsIndexPattern, alertsIndexPattern } from '../../../../common/endpoint/constants';
|
||||
import { validateRelatedEvents } from '../../../../common/endpoint/schema/resolver';
|
||||
import { Fetcher } from './utils/fetch';
|
||||
import { EndpointAppContext } from '../../types';
|
||||
|
||||
export function handleRelatedEvents(
|
||||
log: Logger,
|
||||
endpointAppContext: EndpointAppContext
|
||||
): RequestHandler<
|
||||
TypeOf<typeof validateRelatedEvents.params>,
|
||||
TypeOf<typeof validateRelatedEvents.query>,
|
||||
TypeOf<typeof validateRelatedEvents.body>
|
||||
> {
|
||||
return async (context, req, res) => {
|
||||
const {
|
||||
params: { id },
|
||||
query: { events, afterEvent, legacyEndpointID: endpointID },
|
||||
body,
|
||||
} = req;
|
||||
try {
|
||||
const client = context.core.elasticsearch.legacy.client;
|
||||
|
||||
const fetcher = new Fetcher(client, id, eventsIndexPattern, alertsIndexPattern, endpointID);
|
||||
|
||||
return res.ok({
|
||||
body: await fetcher.events(events, afterEvent, body?.filter),
|
||||
});
|
||||
} catch (err) {
|
||||
log.warn(err);
|
||||
return res.internalError({ body: err });
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
/**
|
||||
* @deprecated msearch functionality for querying events will be removed shortly
|
||||
*/
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
import { ILegacyScopedClusterClient } from 'kibana/server';
|
||||
import { SafeResolverRelatedEvents, SafeResolverEvent } from '../../../../../common/endpoint/types';
|
||||
import { createRelatedEvents } from './node';
|
||||
import { EventsQuery } from '../queries/related_events';
|
||||
import { PaginationBuilder } from './pagination';
|
||||
import { QueryInfo } from '../queries/multi_searcher';
|
||||
import { SingleQueryHandler } from './fetch';
|
||||
|
||||
/**
|
||||
* Parameters for the RelatedEventsQueryHandler
|
||||
*/
|
||||
export interface RelatedEventsParams {
|
||||
limit: number;
|
||||
entityID: string;
|
||||
indexPattern: string;
|
||||
after?: string;
|
||||
legacyEndpointID?: string;
|
||||
filter?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This retrieves the related events for the origin node of a resolver tree.
|
||||
*/
|
||||
export class RelatedEventsQueryHandler implements SingleQueryHandler<SafeResolverRelatedEvents> {
|
||||
private relatedEvents: SafeResolverRelatedEvents | undefined;
|
||||
private readonly query: EventsQuery;
|
||||
private readonly limit: number;
|
||||
private readonly entityID: string;
|
||||
|
||||
constructor(options: RelatedEventsParams) {
|
||||
this.limit = options.limit;
|
||||
this.entityID = options.entityID;
|
||||
|
||||
this.query = new EventsQuery(
|
||||
PaginationBuilder.createBuilder(this.limit, options.after),
|
||||
options.indexPattern,
|
||||
options.legacyEndpointID,
|
||||
options.filter
|
||||
);
|
||||
}
|
||||
|
||||
private handleResponse = (response: SearchResponse<SafeResolverEvent>) => {
|
||||
const results = this.query.formatResponse(response);
|
||||
this.relatedEvents = createRelatedEvents(
|
||||
this.entityID,
|
||||
results,
|
||||
PaginationBuilder.buildCursorRequestLimit(this.limit, results)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a query to use in a msearch.
|
||||
*/
|
||||
nextQuery(): QueryInfo | undefined {
|
||||
if (this.getResults()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
query: this.query,
|
||||
ids: this.entityID,
|
||||
handler: this.handleResponse,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the results after an msearch.
|
||||
*/
|
||||
getResults() {
|
||||
return this.relatedEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a normal search and return the related events results.
|
||||
*
|
||||
* @param client the elasticsearch client
|
||||
*/
|
||||
async search(client: ILegacyScopedClusterClient) {
|
||||
const results = this.getResults();
|
||||
if (results) {
|
||||
return results;
|
||||
}
|
||||
|
||||
this.handleResponse(await this.query.search(client, this.entityID));
|
||||
return this.getResults() ?? createRelatedEvents(this.entityID);
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@
|
|||
import { ILegacyScopedClusterClient } from 'kibana/server';
|
||||
import {
|
||||
SafeResolverChildren,
|
||||
SafeResolverRelatedEvents,
|
||||
SafeResolverAncestry,
|
||||
ResolverRelatedAlerts,
|
||||
SafeResolverLifecycleNode,
|
||||
|
@ -18,7 +17,6 @@ import { StatsQuery } from '../queries/stats';
|
|||
import { createLifecycle } from './node';
|
||||
import { MultiSearcher, QueryInfo } from '../queries/multi_searcher';
|
||||
import { AncestryQueryHandler } from './ancestry_query_handler';
|
||||
import { RelatedEventsQueryHandler } from './events_query_handler';
|
||||
import { RelatedAlertsQueryHandler } from './alerts_query_handler';
|
||||
import { ChildrenStartQueryHandler } from './children_start_query_handler';
|
||||
import { ChildrenLifecycleQueryHandler } from './children_lifecycle_query_handler';
|
||||
|
@ -110,14 +108,6 @@ export class Fetcher {
|
|||
this.endpointID
|
||||
);
|
||||
|
||||
const eventsHandler = new RelatedEventsQueryHandler({
|
||||
limit: options.events,
|
||||
entityID: this.id,
|
||||
after: options.afterEvent,
|
||||
indexPattern: this.eventsIndexPattern,
|
||||
legacyEndpointID: this.endpointID,
|
||||
});
|
||||
|
||||
const alertsHandler = new RelatedAlertsQueryHandler({
|
||||
limit: options.alerts,
|
||||
entityID: this.id,
|
||||
|
@ -139,7 +129,6 @@ export class Fetcher {
|
|||
const msearch = new MultiSearcher(this.client);
|
||||
|
||||
let queries: QueryInfo[] = [];
|
||||
addQueryToList(eventsHandler, queries);
|
||||
addQueryToList(alertsHandler, queries);
|
||||
addQueryToList(childrenHandler, queries);
|
||||
addQueryToList(originHandler, queries);
|
||||
|
@ -176,7 +165,6 @@ export class Fetcher {
|
|||
|
||||
const tree = new Tree(this.id, {
|
||||
ancestry: ancestryHandler.getResults(),
|
||||
relatedEvents: eventsHandler.getResults(),
|
||||
relatedAlerts: alertsHandler.getResults(),
|
||||
children: childrenLifecycleHandler.getResults(),
|
||||
});
|
||||
|
@ -225,31 +213,6 @@ export class Fetcher {
|
|||
return childrenLifecycleHandler.search(this.client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the related events for the origin node.
|
||||
*
|
||||
* @param limit the upper bound number of related events to return. The limit is applied after the cursor is used to
|
||||
* skip the previous results.
|
||||
* @param after a cursor to use as the starting point for retrieving related events
|
||||
* @param filter a kql query for filtering the results
|
||||
*/
|
||||
public async events(
|
||||
limit: number,
|
||||
after?: string,
|
||||
filter?: string
|
||||
): Promise<SafeResolverRelatedEvents> {
|
||||
const eventsHandler = new RelatedEventsQueryHandler({
|
||||
limit,
|
||||
entityID: this.id,
|
||||
after,
|
||||
indexPattern: this.eventsIndexPattern,
|
||||
legacyEndpointID: this.endpointID,
|
||||
filter,
|
||||
});
|
||||
|
||||
return eventsHandler.search(this.client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the alerts for the origin node.
|
||||
*
|
||||
|
|
|
@ -12,25 +12,9 @@ import {
|
|||
SafeResolverLifecycleNode,
|
||||
SafeResolverEvent,
|
||||
SafeResolverChildNode,
|
||||
SafeResolverRelatedEvents,
|
||||
ResolverPaginatedEvents,
|
||||
} from '../../../../../common/endpoint/types';
|
||||
|
||||
/**
|
||||
* Creates a related event object that the related events handler would return
|
||||
*
|
||||
* @param entityID the entity_id for these related events
|
||||
* @param events array of related events
|
||||
* @param nextEvent the cursor to retrieve the next related event
|
||||
*/
|
||||
export function createRelatedEvents(
|
||||
entityID: string,
|
||||
events: SafeResolverEvent[] = [],
|
||||
nextEvent: string | null = null
|
||||
): SafeResolverRelatedEvents {
|
||||
return { entityID, events, nextEvent };
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object that the events handler would return
|
||||
*
|
||||
|
@ -116,10 +100,6 @@ export function createTree(entityID: string): SafeResolverTree {
|
|||
childNodes: [],
|
||||
nextChild: null,
|
||||
},
|
||||
relatedEvents: {
|
||||
events: [],
|
||||
nextEvent: null,
|
||||
},
|
||||
relatedAlerts: {
|
||||
alerts: [],
|
||||
nextAlert: null,
|
||||
|
|
|
@ -6,11 +6,7 @@
|
|||
|
||||
import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data';
|
||||
import { Tree } from './tree';
|
||||
import {
|
||||
SafeResolverAncestry,
|
||||
SafeResolverEvent,
|
||||
SafeResolverRelatedEvents,
|
||||
} from '../../../../../common/endpoint/types';
|
||||
import { SafeResolverAncestry, SafeResolverEvent } from '../../../../../common/endpoint/types';
|
||||
import { entityIDSafeVersion } from '../../../../../common/endpoint/models/event';
|
||||
|
||||
describe('Tree', () => {
|
||||
|
@ -46,19 +42,4 @@ describe('Tree', () => {
|
|||
expect(tree.render().ancestry.nextAncestor).toEqual('hello');
|
||||
});
|
||||
});
|
||||
|
||||
describe('related events', () => {
|
||||
it('adds related events to the tree', () => {
|
||||
const root = generator.generateEvent();
|
||||
const events: SafeResolverRelatedEvents = {
|
||||
entityID: entityIDSafeVersion(root) ?? '',
|
||||
events: Array.from(generator.relatedEventsGenerator(root)),
|
||||
nextEvent: null,
|
||||
};
|
||||
const tree = new Tree(entityIDSafeVersion(root) ?? '', { relatedEvents: events });
|
||||
const rendered = tree.render();
|
||||
expect(rendered.relatedEvents.nextEvent).toBeNull();
|
||||
expect(rendered.relatedEvents.events).toStrictEqual(events.events);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,6 @@ import _ from 'lodash';
|
|||
import {
|
||||
SafeResolverEvent,
|
||||
ResolverNodeStats,
|
||||
SafeResolverRelatedEvents,
|
||||
SafeResolverAncestry,
|
||||
SafeResolverTree,
|
||||
SafeResolverChildren,
|
||||
|
@ -23,7 +22,6 @@ interface Node {
|
|||
}
|
||||
|
||||
export interface Options {
|
||||
relatedEvents?: SafeResolverRelatedEvents;
|
||||
ancestry?: SafeResolverAncestry;
|
||||
children?: SafeResolverChildren;
|
||||
relatedAlerts?: ResolverRelatedAlerts;
|
||||
|
@ -44,7 +42,6 @@ export class Tree {
|
|||
this.tree = tree;
|
||||
this.cache.set(id, tree);
|
||||
|
||||
this.addRelatedEvents(options.relatedEvents);
|
||||
this.addAncestors(options.ancestry);
|
||||
this.addChildren(options.children);
|
||||
this.addRelatedAlerts(options.relatedAlerts);
|
||||
|
@ -68,20 +65,6 @@ export class Tree {
|
|||
return [...this.cache.keys()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add related events for the tree's origin node. Related events cannot be added for other nodes.
|
||||
*
|
||||
* @param relatedEventsInfo is the related events and pagination information to add to the tree.
|
||||
*/
|
||||
private addRelatedEvents(relatedEventsInfo: SafeResolverRelatedEvents | undefined) {
|
||||
if (!relatedEventsInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.tree.relatedEvents.events = relatedEventsInfo.events;
|
||||
this.tree.relatedEvents.nextEvent = relatedEventsInfo.nextEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add alerts for the tree's origin node. Alerts cannot be added for other nodes.
|
||||
*
|
||||
|
|
|
@ -5,10 +5,7 @@
|
|||
*/
|
||||
import expect from '@kbn/expect';
|
||||
import { eventIDSafeVersion } from '../../../../plugins/security_solution/common/endpoint/models/event';
|
||||
import {
|
||||
ResolverPaginatedEvents,
|
||||
SafeResolverRelatedEvents,
|
||||
} from '../../../../plugins/security_solution/common/endpoint/types';
|
||||
import { ResolverPaginatedEvents } from '../../../../plugins/security_solution/common/endpoint/types';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
import {
|
||||
Tree,
|
||||
|
@ -20,7 +17,6 @@ import { compareArrays } from './common';
|
|||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
const resolver = getService('resolverGenerator');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
const relatedEventsToGen = [
|
||||
{ category: RelatedEventCategory.Driver, count: 2 },
|
||||
|
@ -44,308 +40,136 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
ancestryArraySize: 2,
|
||||
};
|
||||
|
||||
describe('event routes', () => {
|
||||
describe('related events route', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('endpoint/resolver/api_feature');
|
||||
resolverTrees = await resolver.createTrees(treeOptions);
|
||||
// we only requested a single alert so there's only 1 tree
|
||||
tree = resolverTrees.trees[0];
|
||||
});
|
||||
after(async () => {
|
||||
await resolver.deleteData(resolverTrees);
|
||||
await esArchiver.unload('endpoint/resolver/api_feature');
|
||||
});
|
||||
|
||||
describe('legacy events', () => {
|
||||
const endpointID = '5a0c957f-b8e7-4538-965e-57e8bb86ad3a';
|
||||
const entityID = '94042';
|
||||
const cursor = 'eyJ0aW1lc3RhbXAiOjE1ODE0NTYyNTUwMDAsImV2ZW50SUQiOiI5NDA0MyJ9';
|
||||
|
||||
it('should return details for the root node', async () => {
|
||||
const { body }: { body: SafeResolverRelatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/${entityID}/events?legacyEndpointID=${endpointID}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(1);
|
||||
expect(body.entityID).to.eql(entityID);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
|
||||
it('returns no values when there is no more data', async () => {
|
||||
const { body }: { body: SafeResolverRelatedEvents } = await supertest
|
||||
// after is set to the document id of the last event so there shouldn't be any more after it
|
||||
.post(
|
||||
`/api/endpoint/resolver/${entityID}/events?legacyEndpointID=${endpointID}&afterEvent=${cursor}`
|
||||
)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200);
|
||||
expect(body.events).be.empty();
|
||||
expect(body.entityID).to.eql(entityID);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
|
||||
it('should return the first page of information when the cursor is invalid', async () => {
|
||||
const { body }: { body: SafeResolverRelatedEvents } = await supertest
|
||||
.post(
|
||||
`/api/endpoint/resolver/${entityID}/events?legacyEndpointID=${endpointID}&afterEvent=blah`
|
||||
)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200);
|
||||
expect(body.entityID).to.eql(entityID);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
|
||||
it('should return no results for an invalid endpoint ID', async () => {
|
||||
const { body }: { body: SafeResolverRelatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/${entityID}/events?legacyEndpointID=foo`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
expect(body.entityID).to.eql(entityID);
|
||||
expect(body.events).to.be.empty();
|
||||
});
|
||||
|
||||
it('should error on invalid pagination values', async () => {
|
||||
await supertest
|
||||
.post(`/api/endpoint/resolver/${entityID}/events?events=0`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(400);
|
||||
await supertest
|
||||
.post(`/api/endpoint/resolver/${entityID}/events?events=20000`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(400);
|
||||
await supertest
|
||||
.post(`/api/endpoint/resolver/${entityID}/events?events=-1`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('endpoint events', () => {
|
||||
it('should not find any events', async () => {
|
||||
const { body }: { body: SafeResolverRelatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/5555/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
expect(body.events).to.be.empty();
|
||||
});
|
||||
|
||||
it('should return details for the root node', async () => {
|
||||
const { body }: { body: SafeResolverRelatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/${tree.origin.id}/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(4);
|
||||
compareArrays(tree.origin.relatedEvents, body.events, true);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
|
||||
it('should allow for the events to be filtered', async () => {
|
||||
const filter = `event.category:"${RelatedEventCategory.Driver}"`;
|
||||
const { body }: { body: SafeResolverRelatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/${tree.origin.id}/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(2);
|
||||
compareArrays(tree.origin.relatedEvents, body.events);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
for (const event of body.events) {
|
||||
expect(event.event?.category).to.be(RelatedEventCategory.Driver);
|
||||
}
|
||||
});
|
||||
|
||||
it('should return paginated results for the root node', async () => {
|
||||
let { body }: { body: SafeResolverRelatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/${tree.origin.id}/events?events=2`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(2);
|
||||
compareArrays(tree.origin.relatedEvents, body.events);
|
||||
expect(body.nextEvent).not.to.eql(null);
|
||||
|
||||
({ body } = await supertest
|
||||
.post(
|
||||
`/api/endpoint/resolver/${tree.origin.id}/events?events=2&afterEvent=${body.nextEvent}`
|
||||
)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200));
|
||||
expect(body.events.length).to.eql(2);
|
||||
compareArrays(tree.origin.relatedEvents, body.events);
|
||||
expect(body.nextEvent).to.not.eql(null);
|
||||
|
||||
({ body } = await supertest
|
||||
.post(
|
||||
`/api/endpoint/resolver/${tree.origin.id}/events?events=2&afterEvent=${body.nextEvent}`
|
||||
)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200));
|
||||
expect(body.events).to.be.empty();
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
|
||||
it('should return the first page of information when the cursor is invalid', async () => {
|
||||
const { body }: { body: SafeResolverRelatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/${tree.origin.id}/events?afterEvent=blah`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(4);
|
||||
compareArrays(tree.origin.relatedEvents, body.events, true);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
|
||||
it('should sort the events in descending order', async () => {
|
||||
const { body }: { body: SafeResolverRelatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/${tree.origin.id}/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(4);
|
||||
// these events are created in the order they are defined in the array so the newest one is
|
||||
// the last element in the array so let's reverse it
|
||||
const relatedEvents = tree.origin.relatedEvents.reverse();
|
||||
for (let i = 0; i < body.events.length; i++) {
|
||||
expect(body.events[i].event?.category).to.equal(relatedEvents[i].event?.category);
|
||||
expect(eventIDSafeVersion(body.events[i])).to.equal(relatedEvents[i].event?.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('event route', () => {
|
||||
let entityIDFilter: string | undefined;
|
||||
before(async () => {
|
||||
resolverTrees = await resolver.createTrees(treeOptions);
|
||||
// we only requested a single alert so there's only 1 tree
|
||||
tree = resolverTrees.trees[0];
|
||||
entityIDFilter = `process.entity_id:"${tree.origin.id}" and not event.category:"process"`;
|
||||
});
|
||||
after(async () => {
|
||||
await resolver.deleteData(resolverTrees);
|
||||
});
|
||||
|
||||
describe('kql events route', () => {
|
||||
let entityIDFilter: string | undefined;
|
||||
before(async () => {
|
||||
resolverTrees = await resolver.createTrees(treeOptions);
|
||||
// we only requested a single alert so there's only 1 tree
|
||||
tree = resolverTrees.trees[0];
|
||||
entityIDFilter = `process.entity_id:"${tree.origin.id}" and not event.category:"process"`;
|
||||
});
|
||||
after(async () => {
|
||||
await resolver.deleteData(resolverTrees);
|
||||
});
|
||||
it('should filter events by event.id', async () => {
|
||||
const { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: `event.id:"${tree.origin.relatedEvents[0]?.event?.id}"`,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(1);
|
||||
expect(tree.origin.relatedEvents[0]?.event?.id).to.eql(body.events[0].event?.id);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
|
||||
it('should filter events by event.id', async () => {
|
||||
const { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: `event.id:"${tree.origin.relatedEvents[0]?.event?.id}"`,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(1);
|
||||
expect(tree.origin.relatedEvents[0]?.event?.id).to.eql(body.events[0].event?.id);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
it('should not find any events when given an invalid entity id', async () => {
|
||||
const { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: 'process.entity_id:"5555"',
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
expect(body.events).to.be.empty();
|
||||
});
|
||||
|
||||
it('should not find any events when given an invalid entity id', async () => {
|
||||
const { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: 'process.entity_id:"5555"',
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
expect(body.events).to.be.empty();
|
||||
});
|
||||
it('should return related events for the root node', async () => {
|
||||
const { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: entityIDFilter,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(4);
|
||||
compareArrays(tree.origin.relatedEvents, body.events, true);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
|
||||
it('should return related events for the root node', async () => {
|
||||
const { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: entityIDFilter,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(4);
|
||||
compareArrays(tree.origin.relatedEvents, body.events, true);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
it('should allow for the events to be filtered', async () => {
|
||||
const filter = `event.category:"${RelatedEventCategory.Driver}" and ${entityIDFilter}`;
|
||||
const { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(2);
|
||||
compareArrays(tree.origin.relatedEvents, body.events);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
for (const event of body.events) {
|
||||
expect(event.event?.category).to.be(RelatedEventCategory.Driver);
|
||||
}
|
||||
});
|
||||
|
||||
it('should allow for the events to be filtered', async () => {
|
||||
const filter = `event.category:"${RelatedEventCategory.Driver}" and ${entityIDFilter}`;
|
||||
const { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(2);
|
||||
compareArrays(tree.origin.relatedEvents, body.events);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
for (const event of body.events) {
|
||||
expect(event.event?.category).to.be(RelatedEventCategory.Driver);
|
||||
}
|
||||
});
|
||||
it('should return paginated results for the root node', async () => {
|
||||
let { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events?limit=2`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: entityIDFilter,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(2);
|
||||
compareArrays(tree.origin.relatedEvents, body.events);
|
||||
expect(body.nextEvent).not.to.eql(null);
|
||||
|
||||
it('should return paginated results for the root node', async () => {
|
||||
let { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events?limit=2`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: entityIDFilter,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(2);
|
||||
compareArrays(tree.origin.relatedEvents, body.events);
|
||||
expect(body.nextEvent).not.to.eql(null);
|
||||
({ body } = await supertest
|
||||
.post(`/api/endpoint/resolver/events?limit=2&afterEvent=${body.nextEvent}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: entityIDFilter,
|
||||
})
|
||||
.expect(200));
|
||||
expect(body.events.length).to.eql(2);
|
||||
compareArrays(tree.origin.relatedEvents, body.events);
|
||||
expect(body.nextEvent).to.not.eql(null);
|
||||
|
||||
({ body } = await supertest
|
||||
.post(`/api/endpoint/resolver/events?limit=2&afterEvent=${body.nextEvent}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: entityIDFilter,
|
||||
})
|
||||
.expect(200));
|
||||
expect(body.events.length).to.eql(2);
|
||||
compareArrays(tree.origin.relatedEvents, body.events);
|
||||
expect(body.nextEvent).to.not.eql(null);
|
||||
({ body } = await supertest
|
||||
.post(`/api/endpoint/resolver/events?limit=2&afterEvent=${body.nextEvent}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: entityIDFilter,
|
||||
})
|
||||
.expect(200));
|
||||
expect(body.events).to.be.empty();
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
|
||||
({ body } = await supertest
|
||||
.post(`/api/endpoint/resolver/events?limit=2&afterEvent=${body.nextEvent}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: entityIDFilter,
|
||||
})
|
||||
.expect(200));
|
||||
expect(body.events).to.be.empty();
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
it('should return the first page of information when the cursor is invalid', async () => {
|
||||
const { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events?afterEvent=blah`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: entityIDFilter,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(4);
|
||||
compareArrays(tree.origin.relatedEvents, body.events, true);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
|
||||
it('should return the first page of information when the cursor is invalid', async () => {
|
||||
const { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events?afterEvent=blah`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: entityIDFilter,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(4);
|
||||
compareArrays(tree.origin.relatedEvents, body.events, true);
|
||||
expect(body.nextEvent).to.eql(null);
|
||||
});
|
||||
|
||||
it('should sort the events in descending order', async () => {
|
||||
const { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: entityIDFilter,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(4);
|
||||
// these events are created in the order they are defined in the array so the newest one is
|
||||
// the last element in the array so let's reverse it
|
||||
const relatedEvents = tree.origin.relatedEvents.reverse();
|
||||
for (let i = 0; i < body.events.length; i++) {
|
||||
expect(body.events[i].event?.category).to.equal(relatedEvents[i].event?.category);
|
||||
expect(eventIDSafeVersion(body.events[i])).to.equal(relatedEvents[i].event?.id);
|
||||
}
|
||||
});
|
||||
it('should sort the events in descending order', async () => {
|
||||
const { body }: { body: ResolverPaginatedEvents } = await supertest
|
||||
.post(`/api/endpoint/resolver/events`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filter: entityIDFilter,
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.events.length).to.eql(4);
|
||||
// these events are created in the order they are defined in the array so the newest one is
|
||||
// the last element in the array so let's reverse it
|
||||
const relatedEvents = tree.origin.relatedEvents.reverse();
|
||||
for (let i = 0; i < body.events.length; i++) {
|
||||
expect(body.events[i].event?.category).to.equal(relatedEvents[i].event?.category);
|
||||
expect(eventIDSafeVersion(body.events[i])).to.equal(relatedEvents[i].event?.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -340,10 +340,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.get(`/api/endpoint/resolver/93933?legacyEndpointID=${endpointID}`)
|
||||
.expect(200);
|
||||
expect(body.ancestry.nextAncestor).to.equal(null);
|
||||
expect(body.relatedEvents.nextEvent).to.equal(null);
|
||||
expect(body.children.nextChild).to.equal(null);
|
||||
expect(body.children.childNodes.length).to.equal(0);
|
||||
expect(body.relatedEvents.events.length).to.equal(0);
|
||||
expect(body.lifecycle.length).to.equal(2);
|
||||
});
|
||||
});
|
||||
|
@ -365,9 +363,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
verifyAncestry(body.ancestry.ancestors, tree, true);
|
||||
verifyLifecycleStats(body.ancestry.ancestors, relatedEventsToGen, relatedAlerts);
|
||||
|
||||
expect(body.relatedEvents.nextEvent).to.equal(null);
|
||||
compareArrays(tree.origin.relatedEvents, body.relatedEvents.events, true);
|
||||
|
||||
expect(body.relatedAlerts.nextAlert).to.equal(null);
|
||||
compareArrays(tree.origin.relatedAlerts, body.relatedAlerts.alerts, true);
|
||||
|
||||
|
|
Loading…
Reference in a new issue