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

[7.x] [Endpoint] Using the stats provided by the backend for resolver UI (#68577) #69070

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
20 changes: 0 additions & 20 deletions x-pack/plugins/security_solution/common/endpoint/models/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,23 +52,3 @@ export function parentEntityId(event: ResolverEvent): string | undefined {
}
return event.process.parent?.entity_id;
}

export function eventType(event: ResolverEvent): string {
// Returning "Process" as a catch-all here because it seems pretty general
let eventCategoryToReturn: string = 'Process';
if (isLegacyEvent(event)) {
const legacyFullType = event.endgame.event_type_full;
if (legacyFullType) {
return legacyFullType;
}
} else {
const eventCategories = event.event.category;
const eventCategory =
typeof eventCategories === 'string' ? eventCategories : eventCategories[0] || '';

if (eventCategory) {
eventCategoryToReturn = eventCategory;
}
}
return eventCategoryToReturn;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { ResolverEvent } from '../../../../common/endpoint/types';
import { RelatedEventDataEntry } from '../../types';
import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types';

interface ServerReturnedResolverData {
readonly type: 'serverReturnedResolverData';
readonly payload: ResolverEvent[];
readonly events: ResolverEvent[];
readonly stats: Map<string, ResolverNodeStats>;
}

interface ServerFailedToReturnResolverData {
readonly type: 'serverFailedToReturnResolverData';
}

/**
* Will occur when a request for related event data is fulfilled by the API.
*/
interface ServerReturnedRelatedEventData {
readonly type: 'serverReturnedRelatedEventData';
readonly payload: Map<ResolverEvent, RelatedEventDataEntry>;
}

/**
* Will occur when a request for related event data is unsuccessful.
*/
Expand All @@ -35,5 +27,4 @@ interface ServerFailedToReturnRelatedEventData {
export type DataAction =
| ServerReturnedResolverData
| ServerFailedToReturnResolverData
| ServerReturnedRelatedEventData
| ServerFailedToReturnRelatedEventData;
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ describe('resolver graph layout', () => {
});
describe('when rendering no nodes', () => {
beforeEach(() => {
const payload: ResolverEvent[] = [];
const action: DataAction = { type: 'serverReturnedResolverData', payload };
const events: ResolverEvent[] = [];
const action: DataAction = { type: 'serverReturnedResolverData', events, stats: new Map() };
store.dispatch(action);
});
it('the graphableProcesses list should only include nothing', () => {
Expand All @@ -127,8 +127,8 @@ describe('resolver graph layout', () => {
});
describe('when rendering one node', () => {
beforeEach(() => {
const payload = [processA];
const action: DataAction = { type: 'serverReturnedResolverData', payload };
const events = [processA];
const action: DataAction = { type: 'serverReturnedResolverData', events, stats: new Map() };
store.dispatch(action);
});
it('the graphableProcesses list should only include nothing', () => {
Expand All @@ -141,8 +141,8 @@ describe('resolver graph layout', () => {
});
describe('when rendering two nodes, one being the parent of the other', () => {
beforeEach(() => {
const payload = [processA, processB];
const action: DataAction = { type: 'serverReturnedResolverData', payload };
const events = [processA, processB];
const action: DataAction = { type: 'serverReturnedResolverData', events, stats: new Map() };
store.dispatch(action);
});
it('the graphableProcesses list should only include nothing', () => {
Expand All @@ -155,7 +155,7 @@ describe('resolver graph layout', () => {
});
describe('when rendering two forks, and one fork has an extra long tine', () => {
beforeEach(() => {
const payload = [
const events = [
processA,
processB,
processC,
Expand All @@ -166,7 +166,7 @@ describe('resolver graph layout', () => {
processH,
processI,
];
const action: DataAction = { type: 'serverReturnedResolverData', payload };
const action: DataAction = { type: 'serverReturnedResolverData', events, stats: new Map() };
store.dispatch(action);
});
it("the graphableProcesses list should only include events with 'processCreated' an 'processRan' eventType", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,21 @@ import { DataState, ResolverAction } from '../../types';
function initialState(): DataState {
return {
results: [],
relatedEventsStats: new Map(),
isLoading: false,
hasError: false,
resultsEnrichedWithRelatedEventInfo: new Map(),
};
}

export const dataReducer: Reducer<DataState, ResolverAction> = (state = initialState(), action) => {
if (action.type === 'serverReturnedResolverData') {
return {
...state,
results: action.payload,
results: action.events,
relatedEventsStats: action.stats,
isLoading: false,
hasError: false,
};
} else if (action.type === 'userRequestedRelatedEventData') {
const resolverEvent = action.payload;
const currentStatsMap = new Map(state.resultsEnrichedWithRelatedEventInfo);
/**
* Set the waiting indicator for this event to indicate that related event results are pending.
* It will be replaced by the actual results from the API when they are returned.
*/
currentStatsMap.set(resolverEvent, 'waitingForRelatedEventData');
return { ...state, resultsEnrichedWithRelatedEventInfo: currentStatsMap };
} else if (action.type === 'serverFailedToReturnRelatedEventData') {
const currentStatsMap = new Map(state.resultsEnrichedWithRelatedEventInfo);
const resolverEvent = action.payload;
currentStatsMap.set(resolverEvent, 'error');
return { ...state, resultsEnrichedWithRelatedEventInfo: currentStatsMap };
} else if (action.type === 'serverReturnedRelatedEventData') {
const relatedDataEntries = new Map([
...state.resultsEnrichedWithRelatedEventInfo,
...action.payload,
]);
return { ...state, resultsEnrichedWithRelatedEventInfo: relatedDataEntries };
} else if (action.type === 'appRequestedResolverData') {
return {
...state,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import {
Matrix3,
AdjacentProcessMap,
Vector2,
RelatedEventData,
RelatedEventDataEntryWithStats,
} from '../../types';
import { ResolverEvent } from '../../../../common/endpoint/types';

Expand Down Expand Up @@ -410,85 +408,11 @@ export const indexedProcessTree = createSelector(graphableProcesses, function in
});

/**
* Process events that will be graphed.
* This returns a map of entity_ids to stats about the related events and alerts.
*/
export const relatedEventResults = function (data: DataState) {
return data.resultsEnrichedWithRelatedEventInfo;
};

/**
* This selector compiles the related event data attached in `relatedEventResults`
* into a `RelatedEventData` map of ResolverEvents to statistics about their related events
*/
export const relatedEventStats = createSelector(relatedEventResults, function getRelatedEvents(
/* eslint-disable no-shadow */
relatedEventResults
/* eslint-enable no-shadow */
) {
/* eslint-disable no-shadow */
const relatedEventStats: RelatedEventData = new Map();
/* eslint-enable no-shadow */
if (!relatedEventResults) {
return relatedEventStats;
}

for (const updatedEvent of relatedEventResults.keys()) {
const newStatsEntry = relatedEventResults.get(updatedEvent);
if (newStatsEntry === 'error') {
// If the entry is an error, return it as is
relatedEventStats.set(updatedEvent, newStatsEntry);
// eslint-disable-next-line no-continue
continue;
}
if (typeof newStatsEntry === 'object') {
/**
* Otherwise, it should be a valid stats entry.
* Do the work to compile the stats.
* Folowing reduction, this will be a record like
* {DNS: 10, File: 2} etc.
*/
const statsForEntry = newStatsEntry?.relatedEvents.reduce(
(compiledStats: Record<string, number>, relatedEvent: { relatedEventType: string }) => {
compiledStats[relatedEvent.relatedEventType] =
(compiledStats[relatedEvent.relatedEventType] || 0) + 1;
return compiledStats;
},
{}
);

const newRelatedEventStats: RelatedEventDataEntryWithStats = Object.assign(newStatsEntry, {
stats: statsForEntry,
});
relatedEventStats.set(updatedEvent, newRelatedEventStats);
}
}
return relatedEventStats;
});

/**
* This selects `RelatedEventData` maps specifically for graphable processes
*/
export const relatedEvents = createSelector(
graphableProcesses,
relatedEventStats,
function getRelatedEvents(
/* eslint-disable no-shadow */
graphableProcesses,
relatedEventStats
/* eslint-enable no-shadow */
) {
const eventsRelatedByProcess: RelatedEventData = new Map();
/* eslint-disable no-shadow */
return graphableProcesses.reduce((relatedEvents, graphableProcess) => {
/* eslint-enable no-shadow */
const relatedEventDataEntry = relatedEventStats?.get(graphableProcess);
if (relatedEventDataEntry) {
relatedEvents.set(graphableProcess, relatedEventDataEntry);
}
return relatedEvents;
}, eventsRelatedByProcess);
}
);
export function relatedEventsStats(data: DataState) {
return data.relatedEventsStats;
}

export const processAdjacencies = createSelector(
indexedProcessTree,
Expand Down
Loading