diff --git a/x-pack/plugins/endpoint/common/models/event.ts b/x-pack/plugins/endpoint/common/models/event.ts
new file mode 100644
index 0000000000000..650486f3c3858
--- /dev/null
+++ b/x-pack/plugins/endpoint/common/models/event.ts
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+import { EndpointEvent, LegacyEndpointEvent } from '../types';
+
+export function isLegacyEvent(
+ event: EndpointEvent | LegacyEndpointEvent
+): event is LegacyEndpointEvent {
+ return (event as LegacyEndpointEvent).endgame !== undefined;
+}
+
+export function eventTimestamp(
+ event: EndpointEvent | LegacyEndpointEvent
+): string | undefined | number {
+ if (isLegacyEvent(event)) {
+ return event.endgame.timestamp_utc;
+ } else {
+ return event['@timestamp'];
+ }
+}
+
+export function eventName(event: EndpointEvent | LegacyEndpointEvent): string {
+ if (isLegacyEvent(event)) {
+ return event.endgame.process_name ? event.endgame.process_name : '';
+ } else {
+ return event.process.name;
+ }
+}
diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts
index aa326c663965d..92bec4f72a387 100644
--- a/x-pack/plugins/endpoint/common/types.ts
+++ b/x-pack/plugins/endpoint/common/types.ts
@@ -311,8 +311,8 @@ export interface EndpointEvent {
version: string;
};
event: {
- category: string;
- type: string;
+ category: string | string[];
+ type: string | string[];
id: string;
kind: string;
};
@@ -328,6 +328,7 @@ export interface EndpointEvent {
name: string;
parent?: {
entity_id: string;
+ name?: string;
};
};
}
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts
index 68731bb3f307f..5e9b08c09c2c7 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts
@@ -165,15 +165,3 @@ export const hasSelectedAlert: (state: AlertListState) => boolean = createSelect
uiQueryParams,
({ selected_alert: selectedAlert }) => selectedAlert !== undefined
);
-
-/**
- * Determine if the alert event is most likely compatible with LegacyEndpointEvent.
- */
-export const selectedAlertIsLegacyEndpointEvent: (
- state: AlertListState
-) => boolean = createSelector(selectedAlertDetailsData, function(event) {
- if (event === undefined) {
- return false;
- }
- return 'endgame' in event;
-});
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx
index 82a4bc00a4396..0ec5a855c8615 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { memo, useMemo } from 'react';
+import styled from 'styled-components';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
@@ -19,87 +20,104 @@ import * as selectors from '../../../../store/alerts/selectors';
import { MetadataPanel } from './metadata_panel';
import { FormattedDate } from '../../formatted_date';
import { AlertDetailResolver } from '../../resolver';
+import { ResolverEvent } from '../../../../../../../common/types';
import { TakeActionDropdown } from './take_action_dropdown';
-export const AlertDetailsOverview = memo(() => {
- const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData);
- if (alertDetailsData === undefined) {
- return null;
- }
- const selectedAlertIsLegacyEndpointEvent = useAlertListSelector(
- selectors.selectedAlertIsLegacyEndpointEvent
- );
+export const AlertDetailsOverview = styled(
+ memo(() => {
+ const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData);
+ if (alertDetailsData === undefined) {
+ return null;
+ }
- const tabs: EuiTabbedContentTab[] = useMemo(() => {
- return [
- {
- id: 'overviewMetadata',
- 'data-test-subj': 'overviewMetadata',
- name: i18n.translate(
- 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.overview',
- {
- defaultMessage: 'Overview',
- }
- ),
- content: (
- <>
-
-
- >
- ),
- },
- {
- id: 'overviewResolver',
- name: i18n.translate(
- 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.resolver',
- {
- defaultMessage: 'Resolver',
- }
- ),
- content: (
- <>
-
- {selectedAlertIsLegacyEndpointEvent && }
- >
- ),
- },
- ];
- }, [selectedAlertIsLegacyEndpointEvent]);
+ const tabs: EuiTabbedContentTab[] = useMemo(() => {
+ return [
+ {
+ id: 'overviewMetadata',
+ 'data-test-subj': 'overviewMetadata',
+ name: i18n.translate(
+ 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.overview',
+ {
+ defaultMessage: 'Overview',
+ }
+ ),
+ content: (
+ <>
+
+
+ >
+ ),
+ },
+ {
+ id: 'overviewResolver',
+ 'data-test-subj': 'overviewResolverTab',
+ name: i18n.translate(
+ 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.resolver',
+ {
+ defaultMessage: 'Resolver',
+ }
+ ),
+ content: (
+ <>
+
+
+ >
+ ),
+ },
+ ];
+ }, [alertDetailsData]);
- return (
- <>
-
-
-
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+ ,
+ }}
+ />
+
+
+
+
+ Endpoint Status:{' '}
+
+ {' '}
+
+
+
+
+ {' '}
-
-
-
-
-
- ,
- }}
- />
-
-
-
-
- Endpoint Status: Online
-
- Alert Status: Open
-
-
-
-
-
- >
- );
-});
+
+
+
+
+
+
+ >
+ );
+ })
+)`
+ height: 100%;
+ width: 100%;
+`;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx
index 52ef480bbb930..d18bc59a35f52 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx
@@ -10,12 +10,12 @@ import { Provider } from 'react-redux';
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
import { Resolver } from '../../../../embeddables/resolver/view';
import { EndpointPluginServices } from '../../../../plugin';
-import { LegacyEndpointEvent } from '../../../../../common/types';
+import { ResolverEvent } from '../../../../../common/types';
import { storeFactory } from '../../../../embeddables/resolver/store';
export const AlertDetailResolver = styled(
React.memo(
- ({ className, selectedEvent }: { className?: string; selectedEvent?: LegacyEndpointEvent }) => {
+ ({ className, selectedEvent }: { className?: string; selectedEvent?: ResolverEvent }) => {
const context = useKibana();
const { store } = storeFactory(context);
@@ -33,4 +33,5 @@ export const AlertDetailResolver = styled(
width: 100%;
display: flex;
flex-grow: 1;
+ min-height: 500px;
`;
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts
index 6892bf11ecff2..c9a03f0a47968 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts
@@ -6,15 +6,15 @@
import { uniquePidForProcess, uniqueParentPidForProcess } from './process_event';
import { IndexedProcessTree } from '../types';
-import { LegacyEndpointEvent } from '../../../../common/types';
+import { ResolverEvent } from '../../../../common/types';
import { levelOrder as baseLevelOrder } from '../lib/tree_sequencers';
/**
* Create a new IndexedProcessTree from an array of ProcessEvents
*/
-export function factory(processes: LegacyEndpointEvent[]): IndexedProcessTree {
- const idToChildren = new Map();
- const idToValue = new Map();
+export function factory(processes: ResolverEvent[]): IndexedProcessTree {
+ const idToChildren = new Map();
+ const idToValue = new Map();
for (const process of processes) {
idToValue.set(uniquePidForProcess(process), process);
@@ -36,10 +36,7 @@ export function factory(processes: LegacyEndpointEvent[]): IndexedProcessTree {
/**
* Returns an array with any children `ProcessEvent`s of the passed in `process`
*/
-export function children(
- tree: IndexedProcessTree,
- process: LegacyEndpointEvent
-): LegacyEndpointEvent[] {
+export function children(tree: IndexedProcessTree, process: ResolverEvent): ResolverEvent[] {
const id = uniquePidForProcess(process);
const processChildren = tree.idToChildren.get(id);
return processChildren === undefined ? [] : processChildren;
@@ -50,8 +47,8 @@ export function children(
*/
export function parent(
tree: IndexedProcessTree,
- childProcess: LegacyEndpointEvent
-): LegacyEndpointEvent | undefined {
+ childProcess: ResolverEvent
+): ResolverEvent | undefined {
const uniqueParentPid = uniqueParentPidForProcess(childProcess);
if (uniqueParentPid === undefined) {
return undefined;
@@ -74,7 +71,7 @@ export function root(tree: IndexedProcessTree) {
if (size(tree) === 0) {
return null;
}
- let current: LegacyEndpointEvent = tree.idToProcess.values().next().value;
+ let current: ResolverEvent = tree.idToProcess.values().next().value;
while (parent(tree, current) !== undefined) {
current = parent(tree, current)!;
}
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts
index 876168d2ed96a..a709d6caf46cb 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts
@@ -4,36 +4,65 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { LegacyEndpointEvent } from '../../../../common/types';
+import { ResolverEvent } from '../../../../common/types';
+import * as event from '../../../../common/models/event';
+import { ResolverProcessType } from '../types';
/**
* Returns true if the process's eventType is either 'processCreated' or 'processRan'.
* Resolver will only render 'graphable' process events.
*/
-export function isGraphableProcess(passedEvent: LegacyEndpointEvent) {
+export function isGraphableProcess(passedEvent: ResolverEvent) {
return eventType(passedEvent) === 'processCreated' || eventType(passedEvent) === 'processRan';
}
+function isValue(field: string | string[], value: string) {
+ if (field instanceof Array) {
+ return field.length === 1 && field[0] === value;
+ } else {
+ return field === value;
+ }
+}
+
/**
* Returns a custom event type for a process event based on the event's metadata.
*/
-export function eventType(passedEvent: LegacyEndpointEvent) {
- const {
- endgame: { event_type_full: type, event_subtype_full: subType },
- } = passedEvent;
+export function eventType(passedEvent: ResolverEvent): ResolverProcessType {
+ if (event.isLegacyEvent(passedEvent)) {
+ const {
+ endgame: { event_type_full: type, event_subtype_full: subType },
+ } = passedEvent;
- if (type === 'process_event') {
- if (subType === 'creation_event' || subType === 'fork_event' || subType === 'exec_event') {
- return 'processCreated';
- } else if (subType === 'already_running') {
- return 'processRan';
- } else if (subType === 'termination_event') {
- return 'processTerminated';
- } else {
- return 'unknownProcessEvent';
+ if (type === 'process_event') {
+ if (subType === 'creation_event' || subType === 'fork_event' || subType === 'exec_event') {
+ return 'processCreated';
+ } else if (subType === 'already_running') {
+ return 'processRan';
+ } else if (subType === 'termination_event') {
+ return 'processTerminated';
+ } else {
+ return 'unknownProcessEvent';
+ }
+ } else if (type === 'alert_event') {
+ return 'processCausedAlert';
+ }
+ } else {
+ const {
+ event: { type, category, kind },
+ } = passedEvent;
+ if (isValue(category, 'process')) {
+ if (isValue(type, 'start') || isValue(type, 'change') || isValue(type, 'creation')) {
+ return 'processCreated';
+ } else if (isValue(type, 'info')) {
+ return 'processRan';
+ } else if (isValue(type, 'end')) {
+ return 'processTerminated';
+ } else {
+ return 'unknownProcessEvent';
+ }
+ } else if (kind === 'alert') {
+ return 'processCausedAlert';
}
- } else if (type === 'alert_event') {
- return 'processCausedAlert';
}
return 'unknownEvent';
}
@@ -41,13 +70,21 @@ export function eventType(passedEvent: LegacyEndpointEvent) {
/**
* Returns the process event's pid
*/
-export function uniquePidForProcess(event: LegacyEndpointEvent) {
- return event.endgame.unique_pid;
+export function uniquePidForProcess(passedEvent: ResolverEvent): string {
+ if (event.isLegacyEvent(passedEvent)) {
+ return String(passedEvent.endgame.unique_pid);
+ } else {
+ return passedEvent.process.entity_id;
+ }
}
/**
* Returns the process event's parent pid
*/
-export function uniqueParentPidForProcess(event: LegacyEndpointEvent) {
- return event.endgame.unique_ppid;
+export function uniqueParentPidForProcess(passedEvent: ResolverEvent): string | undefined {
+ if (event.isLegacyEvent(passedEvent)) {
+ return String(passedEvent.endgame.unique_ppid);
+ } else {
+ return passedEvent.process.parent?.entity_id;
+ }
}
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts
index ecba0ec404d44..fec2078cc60c9 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts
@@ -5,7 +5,7 @@
*/
import { CameraAction } from './camera';
import { DataAction } from './data';
-import { LegacyEndpointEvent } from '../../../../common/types';
+import { ResolverEvent } from '../../../../common/types';
/**
* When the user wants to bring a process node front-and-center on the map.
@@ -16,7 +16,7 @@ interface UserBroughtProcessIntoView {
/**
* Used to identify the process node that should be brought into view.
*/
- readonly process: LegacyEndpointEvent;
+ readonly process: ResolverEvent;
/**
* The time (since epoch in milliseconds) when the action was dispatched.
*/
@@ -33,7 +33,7 @@ interface UserChangedSelectedEvent {
/**
* Optional because they could have unselected the event.
*/
- selectedEvent?: LegacyEndpointEvent;
+ readonly selectedEvent?: ResolverEvent;
};
}
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts
index f34d7c08ce08c..373afa89921dc 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts
@@ -4,14 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { LegacyEndpointEvent } from '../../../../../common/types';
+import { ResolverEvent } from '../../../../../common/types';
interface ServerReturnedResolverData {
readonly type: 'serverReturnedResolverData';
readonly payload: {
readonly data: {
readonly result: {
- readonly search_results: readonly LegacyEndpointEvent[];
+ readonly search_results: readonly ResolverEvent[];
};
};
};
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts
index 304abbb06880b..e8007f82e30c2 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts
@@ -14,7 +14,7 @@ import {
ProcessWithWidthMetadata,
Matrix3,
} from '../../types';
-import { LegacyEndpointEvent } from '../../../../../common/types';
+import { ResolverEvent } from '../../../../../common/types';
import { Vector2 } from '../../types';
import { add as vector2Add, applyMatrix3 } from '../../lib/vector2';
import { isGraphableProcess } from '../../models/process_event';
@@ -112,7 +112,7 @@ export const graphableProcesses = createSelector(
*
*/
function widthsOfProcessSubtrees(indexedProcessTree: IndexedProcessTree): ProcessWidths {
- const widths = new Map();
+ const widths = new Map();
if (size(indexedProcessTree) === 0) {
return widths;
@@ -313,13 +313,13 @@ function processPositions(
indexedProcessTree: IndexedProcessTree,
widths: ProcessWidths
): ProcessPositions {
- const positions = new Map();
+ const positions = new Map();
/**
* This algorithm iterates the tree in level order. It keeps counters that are reset for each parent.
* By keeping track of the last parent node, we can know when we are dealing with a new set of siblings and
* reset the counters.
*/
- let lastProcessedParentNode: LegacyEndpointEvent | undefined;
+ let lastProcessedParentNode: ResolverEvent | undefined;
/**
* Nodes are positioned relative to their siblings. We walk this in level order, so we handle
* children left -> right.
@@ -424,7 +424,7 @@ export const processNodePositionsAndEdgeLineSegments = createSelector(
* Transform the positions of nodes and edges so they seem like they are on an isometric grid.
*/
const transformedEdgeLineSegments: EdgeLineSegment[] = [];
- const transformedPositions = new Map();
+ const transformedPositions = new Map();
for (const [processEvent, position] of positions) {
transformedPositions.set(processEvent, applyMatrix3(position, isometricTransformMatrix));
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/methods.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/methods.ts
index 9f06643626f50..f15307a662388 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/methods.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/methods.ts
@@ -7,7 +7,7 @@
import { animatePanning } from './camera/methods';
import { processNodePositionsAndEdgeLineSegments } from './selectors';
import { ResolverState } from '../types';
-import { LegacyEndpointEvent } from '../../../../common/types';
+import { ResolverEvent } from '../../../../common/types';
const animationDuration = 1000;
@@ -17,7 +17,7 @@ const animationDuration = 1000;
export function animateProcessIntoView(
state: ResolverState,
startTime: number,
- process: LegacyEndpointEvent
+ process: ResolverEvent
): ResolverState {
const { processNodePositions } = processNodePositionsAndEdgeLineSegments(state);
const position = processNodePositions.get(process);
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/middleware.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/middleware.ts
index 900aece60618d..23e4a4fe7d7ed 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/middleware.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/middleware.ts
@@ -8,6 +8,8 @@ import { Dispatch, MiddlewareAPI } from 'redux';
import { KibanaReactContextValue } from '../../../../../../../src/plugins/kibana_react/public';
import { EndpointPluginServices } from '../../../plugin';
import { ResolverState, ResolverAction } from '../types';
+import { ResolverEvent } from '../../../../common/types';
+import * as event from '../../../../common/models/event';
type MiddlewareFactory = (
context?: KibanaReactContextValue
@@ -19,22 +21,54 @@ export const resolverMiddlewareFactory: MiddlewareFactory = context => {
return api => next => async (action: ResolverAction) => {
next(action);
if (action.type === 'userChangedSelectedEvent') {
- if (context?.services.http) {
+ /**
+ * concurrently fetches a process's details, its ancestors, and its related events.
+ */
+ if (context?.services.http && action.payload.selectedEvent) {
api.dispatch({ type: 'appRequestedResolverData' });
- const uniquePid = action.payload.selectedEvent?.endgame?.unique_pid;
- const legacyEndpointID = action.payload.selectedEvent?.agent?.id;
- const [{ lifecycle }, { children }, { events: relatedEvents }] = await Promise.all([
- context.services.http.get(`/api/endpoint/resolver/${uniquePid}`, {
- query: { legacyEndpointID },
- }),
- context.services.http.get(`/api/endpoint/resolver/${uniquePid}/children`, {
- query: { legacyEndpointID },
- }),
- context.services.http.get(`/api/endpoint/resolver/${uniquePid}/related`, {
- query: { legacyEndpointID },
- }),
- ]);
- const response = [...lifecycle, ...children, ...relatedEvents];
+ let response = [];
+ let lifecycle: ResolverEvent[];
+ let childEvents: ResolverEvent[];
+ let relatedEvents: ResolverEvent[];
+ let children = [];
+ const ancestors: ResolverEvent[] = [];
+ const maxAncestors = 5;
+ if (event.isLegacyEvent(action.payload.selectedEvent)) {
+ const uniquePid = action.payload.selectedEvent?.endgame?.unique_pid;
+ const legacyEndpointID = action.payload.selectedEvent?.agent?.id;
+ [{ lifecycle }, { children }, { events: relatedEvents }] = await Promise.all([
+ context.services.http.get(`/api/endpoint/resolver/${uniquePid}`, {
+ query: { legacyEndpointID },
+ }),
+ context.services.http.get(`/api/endpoint/resolver/${uniquePid}/children`, {
+ query: { legacyEndpointID },
+ }),
+ context.services.http.get(`/api/endpoint/resolver/${uniquePid}/related`, {
+ query: { legacyEndpointID },
+ }),
+ ]);
+ childEvents = children.length > 0 ? children.map((child: any) => child.lifecycle) : [];
+ } else {
+ const uniquePid = action.payload.selectedEvent.process.entity_id;
+ const ppid = action.payload.selectedEvent.process.parent?.entity_id;
+ async function getAncestors(pid: string | undefined) {
+ if (ancestors.length < maxAncestors && pid !== undefined) {
+ const parent = await context?.services.http.get(`/api/endpoint/resolver/${pid}`);
+ ancestors.push(parent.lifecycle[0]);
+ if (parent.lifecycle[0].process?.parent?.entity_id) {
+ await getAncestors(parent.lifecycle[0].process.parent.entity_id);
+ }
+ }
+ }
+ [{ lifecycle }, { children }, { events: relatedEvents }] = await Promise.all([
+ context.services.http.get(`/api/endpoint/resolver/${uniquePid}`),
+ context.services.http.get(`/api/endpoint/resolver/${uniquePid}/children`),
+ context.services.http.get(`/api/endpoint/resolver/${uniquePid}/related`),
+ getAncestors(ppid),
+ ]);
+ }
+ childEvents = children.length > 0 ? children.map((child: any) => child.lifecycle) : [];
+ response = [...lifecycle, ...childEvents, ...relatedEvents, ...ancestors];
api.dispatch({
type: 'serverReturnedResolverData',
payload: { data: { result: { search_results: response } } },
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts
index 4c2a1ea5ac21f..4380d3ab98999 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts
@@ -8,7 +8,7 @@ import { Store } from 'redux';
import { ResolverAction } from './store/actions';
export { ResolverAction } from './store/actions';
-import { LegacyEndpointEvent } from '../../../common/types';
+import { ResolverEvent } from '../../../common/types';
/**
* Redux state for the Resolver feature. Properties on this interface are populated via multiple reducers using redux's `combineReducers`.
@@ -115,7 +115,7 @@ export type CameraState = {
* State for `data` reducer which handles receiving Resolver data from the backend.
*/
export interface DataState {
- readonly results: readonly LegacyEndpointEvent[];
+ readonly results: readonly ResolverEvent[];
isLoading: boolean;
}
@@ -184,21 +184,21 @@ export interface IndexedProcessTree {
/**
* Map of ID to a process's children
*/
- idToChildren: Map;
+ idToChildren: Map;
/**
* Map of ID to process
*/
- idToProcess: Map;
+ idToProcess: Map;
}
/**
* A map of ProcessEvents (representing process nodes) to the 'width' of their subtrees as calculated by `widthsOfProcessSubtrees`
*/
-export type ProcessWidths = Map;
+export type ProcessWidths = Map;
/**
* Map of ProcessEvents (representing process nodes) to their positions. Calculated by `processPositions`
*/
-export type ProcessPositions = Map;
+export type ProcessPositions = Map;
/**
* An array of vectors2 forming an polyline. Used to connect process nodes in the graph.
*/
@@ -208,11 +208,11 @@ export type EdgeLineSegment = Vector2[];
* Used to provide precalculated info from `widthsOfProcessSubtrees`. These 'width' values are used in the layout of the graph.
*/
export type ProcessWithWidthMetadata = {
- process: LegacyEndpointEvent;
+ process: ResolverEvent;
width: number;
} & (
| {
- parent: LegacyEndpointEvent;
+ parent: ResolverEvent;
parentWidth: number;
isOnlyChild: boolean;
firstChildWidth: number;
@@ -275,4 +275,15 @@ export interface SideEffectSimulator {
mock: jest.Mocked> & Pick;
}
+/**
+ * The internal types of process events used by resolver, mapped from v0 and v1 events.
+ */
+export type ResolverProcessType =
+ | 'processCreated'
+ | 'processRan'
+ | 'processTerminated'
+ | 'unknownProcessEvent'
+ | 'processCausedAlert'
+ | 'unknownEvent';
+
export type ResolverStore = Store;
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx
index 52a0872f269f5..eab22f993d0a8 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx
@@ -15,7 +15,7 @@ import { GraphControls } from './graph_controls';
import { ProcessEventDot } from './process_event_dot';
import { useCamera } from './use_camera';
import { ResolverAction } from '../types';
-import { LegacyEndpointEvent } from '../../../../common/types';
+import { ResolverEvent } from '../../../../common/types';
const StyledPanel = styled(Panel)`
position: absolute;
@@ -39,7 +39,7 @@ export const Resolver = styled(
selectedEvent,
}: {
className?: string;
- selectedEvent?: LegacyEndpointEvent;
+ selectedEvent?: ResolverEvent;
}) {
const { processNodePositions, edgeLineSegments } = useSelector(
selectors.processNodePositionsAndEdgeLineSegments
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/panel.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/panel.tsx
index 84c299698bb32..1250c1106b355 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/panel.tsx
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/panel.tsx
@@ -11,7 +11,8 @@ import euiVars from '@elastic/eui/dist/eui_theme_light.json';
import { useSelector } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { SideEffectContext } from './side_effect_context';
-import { LegacyEndpointEvent } from '../../../../common/types';
+import { ResolverEvent } from '../../../../common/types';
+import * as event from '../../../../common/models/event';
import { useResolverDispatch } from './use_resolver_dispatch';
import * as selectors from '../store/selectors';
@@ -38,7 +39,7 @@ export const Panel = memo(function Event({ className }: { className?: string })
interface ProcessTableView {
name: string;
timestamp?: Date;
- event: LegacyEndpointEvent;
+ event: ResolverEvent;
}
const { processNodePositions } = useSelector(selectors.processNodePositionsAndEdgeLineSegments);
@@ -48,14 +49,16 @@ export const Panel = memo(function Event({ className }: { className?: string })
() =>
[...processNodePositions.keys()].map(processEvent => {
let dateTime;
- if (processEvent.endgame.timestamp_utc) {
- const date = new Date(processEvent.endgame.timestamp_utc);
+ const eventTime = event.eventTimestamp(processEvent);
+ const name = event.eventName(processEvent);
+ if (eventTime) {
+ const date = new Date(eventTime);
if (isFinite(date.getTime())) {
dateTime = date;
}
}
return {
- name: processEvent.endgame.process_name ? processEvent.endgame.process_name : '',
+ name,
timestamp: dateTime,
event: processEvent,
};
@@ -115,9 +118,9 @@ export const Panel = memo(function Event({ className }: { className?: string })
}),
dataType: 'date',
sortable: true,
- render(eventTimestamp?: Date) {
- return eventTimestamp ? (
- formatter.format(eventTimestamp)
+ render(eventDate?: Date) {
+ return eventDate ? (
+ formatter.format(eventDate)
) : (
{i18n.translate('xpack.endpoint.resolver.panel.tabel.row.timestampInvalidLabel', {
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx
index 034780c7ba14c..2241df97291ae 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx
@@ -8,7 +8,8 @@ import React from 'react';
import styled from 'styled-components';
import { applyMatrix3 } from '../lib/vector2';
import { Vector2, Matrix3 } from '../types';
-import { LegacyEndpointEvent } from '../../../../common/types';
+import { ResolverEvent } from '../../../../common/types';
+import * as eventModel from '../../../../common/models/event';
/**
* A placeholder view for a process node.
@@ -32,7 +33,7 @@ export const ProcessEventDot = styled(
/**
* An event which contains details about the process node.
*/
- event: LegacyEndpointEvent;
+ event: ResolverEvent;
/**
* projectionMatrix which can be used to convert `position` to screen coordinates.
*/
@@ -42,14 +43,13 @@ export const ProcessEventDot = styled(
* Convert the position, which is in 'world' coordinates, to screen coordinates.
*/
const [left, top] = applyMatrix3(position, projectionMatrix);
-
const style = {
left: (left - 20).toString() + 'px',
top: (top - 20).toString() + 'px',
};
return (
-
- name: {event.endgame.process_name}
+
+ name: {eventModel.eventName(event)}
x: {position[0]}
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx
index 711e4f9a5c537..6e83fc19a922e 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx
@@ -11,7 +11,7 @@ import { Provider } from 'react-redux';
import * as selectors from '../store/selectors';
import { storeFactory } from '../store';
import { Matrix3, ResolverAction, ResolverStore, SideEffectSimulator } from '../types';
-import { LegacyEndpointEvent } from '../../../../common/types';
+import { ResolverEvent } from '../../../../common/types';
import { SideEffectContext } from './side_effect_context';
import { applyMatrix3 } from '../lib/vector2';
import { sideEffectSimulator } from './side_effect_simulator';
@@ -133,9 +133,9 @@ describe('useCamera on an unpainted element', () => {
expect(simulator.mock.requestAnimationFrame).not.toHaveBeenCalled();
});
describe('when the camera begins animation', () => {
- let process: LegacyEndpointEvent;
+ let process: ResolverEvent;
beforeEach(() => {
- const events: LegacyEndpointEvent[] = [];
+ const events: ResolverEvent[] = [];
const numberOfEvents: number = Math.floor(Math.random() * 10 + 1);
for (let index = 0; index < numberOfEvents; index++) {
@@ -164,7 +164,7 @@ describe('useCamera on an unpainted element', () => {
act(() => {
store.dispatch(serverResponseAction);
});
- const processes: LegacyEndpointEvent[] = [
+ const processes: ResolverEvent[] = [
...selectors
.processNodePositionsAndEdgeLineSegments(store.getState())
.processNodePositions.keys(),
diff --git a/x-pack/test/functional/apps/endpoint/alerts.ts b/x-pack/test/functional/apps/endpoint/alerts.ts
index 1ce7eb41e6690..759574702c0f1 100644
--- a/x-pack/test/functional/apps/endpoint/alerts.ts
+++ b/x-pack/test/functional/apps/endpoint/alerts.ts
@@ -18,8 +18,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
await esArchiver.load('endpoint/alerts/api_feature');
await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/alerts');
});
-
- it('loads in the browser', async () => {
+ it('loads the Alert List Page', async () => {
await testSubjects.existOrFail('alertListPage');
});
it('contains the Alert List Page title', async () => {
@@ -57,6 +56,12 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
it('loads the Alert List Flyout correctly', async () => {
await testSubjects.existOrFail('alertDetailFlyout');
});
+
+ it('loads the resolver component and renders at least a single node', async () => {
+ await testSubjects.click('overviewResolverTab');
+ await testSubjects.existOrFail('alertResolver');
+ await testSubjects.existOrFail('resolverNode');
+ });
});
after(async () => {