diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts
index 9e1c396723a27..0826391a10688 100644
--- a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts
+++ b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.test.ts
@@ -13,6 +13,7 @@ import {
mockTreeWithNoAncestorsAnd2Children,
mockTreeWith2AncestorsAndNoChildren,
mockTreeWith1AncestorAnd2ChildrenAndAllNodesHave2GraphableEvents,
+ mockTreeWithAllProcessesTerminated,
} from '../mocks/resolver_tree';
import { uniquePidForProcess } from '../../models/process_event';
import { EndpointEvent } from '../../../../common/endpoint/types';
@@ -299,6 +300,34 @@ describe('data state', () => {
expect(selectors.ariaFlowtoCandidate(state())(secondAncestorID)).toBe(null);
});
});
+ describe('with a tree with all processes terminated', () => {
+ const originID = 'c';
+ const firstAncestorID = 'b';
+ const secondAncestorID = 'a';
+ beforeEach(() => {
+ actions.push({
+ type: 'serverReturnedResolverData',
+ payload: {
+ result: mockTreeWithAllProcessesTerminated({
+ originID,
+ firstAncestorID,
+ secondAncestorID,
+ }),
+ // this value doesn't matter
+ databaseDocumentID: '',
+ },
+ });
+ });
+ it('should have origin as terminated', () => {
+ expect(selectors.isProcessTerminated(state())(originID)).toBe(true);
+ });
+ it('should have first ancestor as termianted', () => {
+ expect(selectors.isProcessTerminated(state())(firstAncestorID)).toBe(true);
+ });
+ it('should have second ancestor as terminated', () => {
+ expect(selectors.isProcessTerminated(state())(secondAncestorID)).toBe(true);
+ });
+ });
describe('with a tree with 2 children and no ancestors', () => {
const originID = 'c';
const firstChildID = 'd';
diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts
index 1d65b406306a3..ea0cb8663d11d 100644
--- a/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts
+++ b/x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts
@@ -105,6 +105,19 @@ export const terminatedProcesses = createSelector(resolverTreeResponse, function
);
});
+/**
+ * A function that given an entity id returns a boolean indicating if the id is in the set of terminated processes.
+ */
+export const isProcessTerminated = createSelector(terminatedProcesses, function (
+ /* eslint-disable no-shadow */
+ terminatedProcesses
+ /* eslint-enable no-shadow */
+) {
+ return (entityId: string) => {
+ return terminatedProcesses.has(entityId);
+ };
+});
+
/**
* Process events that will be graphed.
*/
diff --git a/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts b/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts
index b58ea73e1fdc7..8f2e0ad3a6d85 100644
--- a/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts
+++ b/x-pack/plugins/security_solution/public/resolver/store/mocks/endpoint_event.ts
@@ -14,16 +14,18 @@ export function mockEndpointEvent({
name,
parentEntityId,
timestamp,
+ lifecycleType,
}: {
entityID: string;
name: string;
parentEntityId: string | undefined;
timestamp: number;
+ lifecycleType?: string;
}): EndpointEvent {
return {
'@timestamp': timestamp,
event: {
- type: 'start',
+ type: lifecycleType ? lifecycleType : 'start',
category: 'process',
},
process: {
diff --git a/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts b/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts
index 2860eec5a6ab6..ae43955f4c47c 100644
--- a/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts
+++ b/x-pack/plugins/security_solution/public/resolver/store/mocks/resolver_tree.ts
@@ -46,6 +46,69 @@ export function mockTreeWith2AncestorsAndNoChildren({
} as unknown) as ResolverTree;
}
+export function mockTreeWithAllProcessesTerminated({
+ originID,
+ firstAncestorID,
+ secondAncestorID,
+}: {
+ secondAncestorID: string;
+ firstAncestorID: string;
+ originID: string;
+}): ResolverTree {
+ const secondAncestor: ResolverEvent = mockEndpointEvent({
+ entityID: secondAncestorID,
+ name: 'a',
+ parentEntityId: 'none',
+ timestamp: 0,
+ });
+ const firstAncestor: ResolverEvent = mockEndpointEvent({
+ entityID: firstAncestorID,
+ name: 'b',
+ parentEntityId: secondAncestorID,
+ timestamp: 1,
+ });
+ const originEvent: ResolverEvent = mockEndpointEvent({
+ entityID: originID,
+ name: 'c',
+ parentEntityId: firstAncestorID,
+ timestamp: 2,
+ });
+ const secondAncestorTermination: ResolverEvent = mockEndpointEvent({
+ entityID: secondAncestorID,
+ name: 'a',
+ parentEntityId: 'none',
+ timestamp: 0,
+ lifecycleType: 'end',
+ });
+ const firstAncestorTermination: ResolverEvent = mockEndpointEvent({
+ entityID: firstAncestorID,
+ name: 'b',
+ parentEntityId: secondAncestorID,
+ timestamp: 1,
+ lifecycleType: 'end',
+ });
+ const originEventTermination: ResolverEvent = mockEndpointEvent({
+ entityID: originID,
+ name: 'c',
+ parentEntityId: firstAncestorID,
+ timestamp: 2,
+ lifecycleType: 'end',
+ });
+ return ({
+ entityID: originID,
+ children: {
+ childNodes: [],
+ },
+ ancestry: {
+ ancestors: [
+ { lifecycle: [secondAncestor, secondAncestorTermination] },
+ { lifecycle: [firstAncestor, firstAncestorTermination] },
+ ],
+ },
+ lifecycle: [originEvent, originEventTermination],
+ } as unknown) as ResolverTree;
+}
+
export function mockTreeWithNoAncestorsAnd2Children({
originID,
firstChildID,
diff --git a/x-pack/plugins/security_solution/public/resolver/store/selectors.ts b/x-pack/plugins/security_solution/public/resolver/store/selectors.ts
index 66d7e04d118ed..87ef8d5d095ef 100644
--- a/x-pack/plugins/security_solution/public/resolver/store/selectors.ts
+++ b/x-pack/plugins/security_solution/public/resolver/store/selectors.ts
@@ -53,6 +53,14 @@ export const userIsPanning = composeSelectors(cameraStateSelector, cameraSelecto
*/
export const isAnimating = composeSelectors(cameraStateSelector, cameraSelectors.isAnimating);
+/**
+ * Whether or not a given entity id is in the set of termination events.
+ */
+export const isProcessTerminated = composeSelectors(
+ dataStateSelector,
+ dataSelectors.isProcessTerminated
+);
+
/**
* Given a nodeID (aka entity_id) get the indexed process event.
* Legacy functions take process events instead of nodeID, use this to get
diff --git a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx
index cb0acdc29ceb1..83d3930065da6 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx
@@ -162,19 +162,10 @@ const PanelContent = memo(function PanelContent() {
return 'processListWithCounts';
}, [uiSelectedEvent, crumbEvent, crumbId, graphableProcessEntityIds]);
- const terminatedProcesses = useSelector(selectors.terminatedProcesses);
- const processEntityId = uiSelectedEvent ? event.entityId(uiSelectedEvent) : undefined;
- const isProcessTerminated = processEntityId ? terminatedProcesses.has(processEntityId) : false;
-
const panelInstance = useMemo(() => {
if (panelToShow === 'processDetails') {
return (
-
+
);
}
@@ -213,13 +204,7 @@ const PanelContent = memo(function PanelContent() {
);
}
// The default 'Event List' / 'List of all processes' view
- return (
-
- );
+ return ;
}, [
uiSelectedEvent,
crumbEvent,
@@ -227,7 +212,6 @@ const PanelContent = memo(function PanelContent() {
pushToQueryParams,
relatedStatsForIdFromParams,
panelToShow,
- isProcessTerminated,
]);
return <>{panelInstance}>;
diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_detail.tsx
index 5d90cd11d31af..29c7676d2167d 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_detail.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_detail.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 { useSelector } from 'react-redux';
import { i18n } from '@kbn/i18n';
import {
htmlIdGenerator,
@@ -15,6 +16,7 @@ import {
} from '@elastic/eui';
import styled from 'styled-components';
import { FormattedMessage } from 'react-intl';
+import * as selectors from '../../store/selectors';
import * as event from '../../../../common/endpoint/models/event';
import { CrumbInfo, formatDate, StyledBreadcrumbs } from './panel_content_utilities';
import {
@@ -41,16 +43,14 @@ const StyledDescriptionList = styled(EuiDescriptionList)`
*/
export const ProcessDetails = memo(function ProcessDetails({
processEvent,
- isProcessTerminated,
- isProcessOrigin,
pushToQueryParams,
}: {
processEvent: ResolverEvent;
- isProcessTerminated: boolean;
- isProcessOrigin: boolean;
pushToQueryParams: (queryStringKeyValuePair: CrumbInfo) => unknown;
}) {
const processName = event.eventName(processEvent);
+ const entityId = event.entityId(processEvent);
+ const isProcessTerminated = useSelector(selectors.isProcessTerminated)(entityId);
const processInfoEntry = useMemo(() => {
const eventTime = event.eventTimestamp(processEvent);
const dateTime = eventTime ? formatDate(eventTime) : '';
@@ -151,8 +151,8 @@ export const ProcessDetails = memo(function ProcessDetails({
if (!processEvent) {
return { descriptionText: '' };
}
- return cubeAssetsForNode(isProcessTerminated, isProcessOrigin);
- }, [processEvent, cubeAssetsForNode, isProcessTerminated, isProcessOrigin]);
+ return cubeAssetsForNode(isProcessTerminated, false);
+ }, [processEvent, cubeAssetsForNode, isProcessTerminated]);
const titleId = useMemo(() => htmlIdGenerator('resolverTable')(), []);
return (
@@ -161,10 +161,7 @@ export const ProcessDetails = memo(function ProcessDetails({
-
+
{processName}
diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx
index 6f9bfad8c08c2..efb96cde431e5 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx
@@ -50,12 +50,8 @@ const StyledLimitWarning = styled(LimitWarning)`
*/
export const ProcessListWithCounts = memo(function ProcessListWithCounts({
pushToQueryParams,
- isProcessTerminated,
- isProcessOrigin,
}: {
pushToQueryParams: (queryStringKeyValuePair: CrumbInfo) => unknown;
- isProcessTerminated: boolean;
- isProcessOrigin: boolean;
}) {
interface ProcessTableView {
name: string;
@@ -65,6 +61,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({
const dispatch = useResolverDispatch();
const { timestamp } = useContext(SideEffectContext);
+ const isProcessTerminated = useSelector(selectors.isProcessTerminated);
const handleBringIntoViewClick = useCallback(
(processTableViewItem) => {
dispatch({
@@ -92,6 +89,8 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({
sortable: true,
truncateText: true,
render(name: string, item: ProcessTableView) {
+ const entityId = event.entityId(item.event);
+ const isTerminated = isProcessTerminated(entityId);
return name === '' ? (
{i18n.translate(
@@ -108,10 +107,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({
pushToQueryParams({ crumbId: event.entityId(item.event), crumbEvent: '' });
}}
>
-
+
{name}
);
@@ -143,7 +139,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({
},
},
],
- [pushToQueryParams, handleBringIntoViewClick, isProcessOrigin, isProcessTerminated]
+ [pushToQueryParams, handleBringIntoViewClick, isProcessTerminated]
);
const { processNodePositions } = useSelector(selectors.layout);
diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx
index 98eea51a011b6..b073324b27f9b 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx
@@ -13,13 +13,11 @@ import { useResolverTheme } from '../assets';
*/
export const CubeForProcess = memo(function CubeForProcess({
isProcessTerminated,
- isProcessOrigin,
}: {
isProcessTerminated: boolean;
- isProcessOrigin: boolean;
}) {
const { cubeAssetsForNode } = useResolverTheme();
- const { cubeSymbol, descriptionText } = cubeAssetsForNode(isProcessTerminated, isProcessOrigin);
+ const { cubeSymbol, descriptionText } = cubeAssetsForNode(isProcessTerminated, false);
return (
<>