Skip to content

Commit

Permalink
[Endpoint] Using the stats provided by the backend for resolver UI (#…
Browse files Browse the repository at this point in the history
…68577) (#69070)

* Removing a lot of the code to request related events and create the maps

* Correctly displaying stats

* Removing tests

* Addressing todos

* Fixing test

* Fixing camera test

Co-authored-by: Elastic Machine <[email protected]>

Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
jonathan-buttner and elasticmachine authored Jun 13, 2020
1 parent 264b2e1 commit 99d4e36
Show file tree
Hide file tree
Showing 12 changed files with 68 additions and 370 deletions.
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

0 comments on commit 99d4e36

Please sign in to comment.