From 810ff87afbac92aa8b655dc944b27894f1c4682a Mon Sep 17 00:00:00 2001
From: Kevin Qualters <56408403+kqualters-elastic@users.noreply.github.com>
Date: Thu, 6 Aug 2020 16:29:09 -0400
Subject: [PATCH] [Security Solution][Resolver] standardize resolver panel
component naming (#74537)
---
...ess_cube_icon.tsx => cube_for_process.tsx} | 90 +--
...ounts.tsx => event_counts_for_process.tsx} | 288 +++----
.../view/{panel.tsx => panels/index.tsx} | 28 +-
...process_detail.tsx => process_details.tsx} | 2 +-
...elated_list.tsx => process_event_list.tsx} | 580 +++++++-------
..._list.tsx => process_list_with_counts.tsx} | 2 +-
...ed_detail.tsx => related_event_detail.tsx} | 750 +++++++++---------
.../public/resolver/view/styles.tsx | 2 +-
8 files changed, 871 insertions(+), 871 deletions(-)
rename x-pack/plugins/security_solution/public/resolver/view/panels/{process_cube_icon.tsx => cube_for_process.tsx} (96%)
rename x-pack/plugins/security_solution/public/resolver/view/panels/{panel_content_related_counts.tsx => event_counts_for_process.tsx} (97%)
rename x-pack/plugins/security_solution/public/resolver/view/{panel.tsx => panels/index.tsx} (90%)
rename x-pack/plugins/security_solution/public/resolver/view/panels/{panel_content_process_detail.tsx => process_details.tsx} (98%)
rename x-pack/plugins/security_solution/public/resolver/view/panels/{panel_content_related_list.tsx => process_event_list.tsx} (95%)
rename x-pack/plugins/security_solution/public/resolver/view/panels/{panel_content_process_list.tsx => process_list_with_counts.tsx} (99%)
rename x-pack/plugins/security_solution/public/resolver/view/panels/{panel_content_related_detail.tsx => related_event_detail.tsx} (97%)
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/cube_for_process.tsx
similarity index 96%
rename from x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx
rename to x-pack/plugins/security_solution/public/resolver/view/panels/cube_for_process.tsx
index b073324b27f9b..0d8f65b4e39e6 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/cube_for_process.tsx
@@ -1,45 +1,45 @@
-/*
- * 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 React, { memo } from 'react';
-import { useResolverTheme } from '../assets';
-
-/**
- * During user testing, one user indicated they wanted to see stronger visual relationships between
- * Nodes on the graph and what's in the table. Using the same symbol in both places (as below) could help with that.
- */
-export const CubeForProcess = memo(function CubeForProcess({
- isProcessTerminated,
-}: {
- isProcessTerminated: boolean;
-}) {
- const { cubeAssetsForNode } = useResolverTheme();
- const { cubeSymbol, descriptionText } = cubeAssetsForNode(isProcessTerminated, false);
-
- return (
- <>
-
- {descriptionText}
-
-
- >
- );
-});
+/*
+ * 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 React, { memo } from 'react';
+import { useResolverTheme } from '../assets';
+
+/**
+ * During user testing, one user indicated they wanted to see stronger visual relationships between
+ * Nodes on the graph and what's in the table. Using the same symbol in both places (as below) could help with that.
+ */
+export const CubeForProcess = memo(function CubeForProcess({
+ isProcessTerminated,
+}: {
+ isProcessTerminated: boolean;
+}) {
+ const { cubeAssetsForNode } = useResolverTheme();
+ const { cubeSymbol, descriptionText } = cubeAssetsForNode(isProcessTerminated, false);
+
+ return (
+ <>
+
+ {descriptionText}
+
+
+ >
+ );
+});
diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_counts.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/event_counts_for_process.tsx
similarity index 97%
rename from x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_counts.tsx
rename to x-pack/plugins/security_solution/public/resolver/view/panels/event_counts_for_process.tsx
index 880ee1dc7a10a..129aff776808a 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_counts.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/panels/event_counts_for_process.tsx
@@ -1,144 +1,144 @@
-/*
- * 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 React, { memo, useMemo } from 'react';
-import { i18n } from '@kbn/i18n';
-import { EuiBasicTableColumn, EuiButtonEmpty, EuiSpacer, EuiInMemoryTable } from '@elastic/eui';
-import { FormattedMessage } from 'react-intl';
-import { CrumbInfo, StyledBreadcrumbs } from './panel_content_utilities';
-
-import * as event from '../../../../common/endpoint/models/event';
-import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types';
-
-/**
- * This view gives counts for all the related events of a process grouped by related event type.
- * It should look something like:
- *
- * | Count | Event Type |
- * | :--------------------- | :------------------------- |
- * | 5 | DNS |
- * | 12 | Registry |
- * | 2 | Network |
- *
- */
-export const EventCountsForProcess = memo(function EventCountsForProcess({
- processEvent,
- pushToQueryParams,
- relatedStats,
-}: {
- processEvent: ResolverEvent;
- pushToQueryParams: (queryStringKeyValuePair: CrumbInfo) => unknown;
- relatedStats: ResolverNodeStats;
-}) {
- interface EventCountsTableView {
- name: string;
- count: number;
- }
-
- const relatedEventsState = { stats: relatedStats.events.byCategory };
- const processName = processEvent && event.eventName(processEvent);
- const processEntityId = event.entityId(processEvent);
- /**
- * totalCount: This will reflect the aggregated total by category for all related events
- * e.g. [dns,file],[dns,file],[registry] will have an aggregate total of 5. This is to keep the
- * total number consistent with the "broken out" totals we see elsewhere in the app.
- * E.g. on the rleated list by type, the above would show as:
- * 2 dns
- * 2 file
- * 1 registry
- * So it would be extremely disorienting to show the user a "3" above that as a total.
- */
- const totalCount = Object.values(relatedStats.events.byCategory).reduce(
- (sum, val) => sum + val,
- 0
- );
- const eventsString = i18n.translate(
- 'xpack.securitySolution.endpoint.resolver.panel.processEventCounts.events',
- {
- defaultMessage: 'Events',
- }
- );
- const crumbs = useMemo(() => {
- return [
- {
- text: eventsString,
- onClick: () => {
- pushToQueryParams({ crumbId: '', crumbEvent: '' });
- },
- },
- {
- text: processName,
- onClick: () => {
- pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' });
- },
- },
- {
- text: (
- <>
-
- >
- ),
- onClick: () => {
- pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' });
- },
- },
- ];
- }, [processName, totalCount, processEntityId, pushToQueryParams, eventsString]);
- const rows = useMemo(() => {
- return Object.entries(relatedEventsState.stats).map(
- ([eventType, count]): EventCountsTableView => {
- return {
- name: eventType,
- count,
- };
- }
- );
- }, [relatedEventsState]);
- const columns = useMemo>>(
- () => [
- {
- field: 'count',
- name: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.table.row.count', {
- defaultMessage: 'Count',
- }),
- width: '20%',
- sortable: true,
- },
- {
- field: 'name',
- name: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.table.row.eventType', {
- defaultMessage: 'Event Type',
- }),
- width: '80%',
- sortable: true,
- render(name: string) {
- return (
- {
- pushToQueryParams({ crumbId: event.entityId(processEvent), crumbEvent: name });
- }}
- >
- {name}
-
- );
- },
- },
- ],
- [pushToQueryParams, processEvent]
- );
- return (
- <>
-
-
- items={rows} columns={columns} sorting />
- >
- );
-});
-EventCountsForProcess.displayName = 'EventCountsForProcess';
+/*
+ * 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 React, { memo, useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiBasicTableColumn, EuiButtonEmpty, EuiSpacer, EuiInMemoryTable } from '@elastic/eui';
+import { FormattedMessage } from 'react-intl';
+import { CrumbInfo, StyledBreadcrumbs } from './panel_content_utilities';
+
+import * as event from '../../../../common/endpoint/models/event';
+import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types';
+
+/**
+ * This view gives counts for all the related events of a process grouped by related event type.
+ * It should look something like:
+ *
+ * | Count | Event Type |
+ * | :--------------------- | :------------------------- |
+ * | 5 | DNS |
+ * | 12 | Registry |
+ * | 2 | Network |
+ *
+ */
+export const EventCountsForProcess = memo(function EventCountsForProcess({
+ processEvent,
+ pushToQueryParams,
+ relatedStats,
+}: {
+ processEvent: ResolverEvent;
+ pushToQueryParams: (queryStringKeyValuePair: CrumbInfo) => unknown;
+ relatedStats: ResolverNodeStats;
+}) {
+ interface EventCountsTableView {
+ name: string;
+ count: number;
+ }
+
+ const relatedEventsState = { stats: relatedStats.events.byCategory };
+ const processName = processEvent && event.eventName(processEvent);
+ const processEntityId = event.entityId(processEvent);
+ /**
+ * totalCount: This will reflect the aggregated total by category for all related events
+ * e.g. [dns,file],[dns,file],[registry] will have an aggregate total of 5. This is to keep the
+ * total number consistent with the "broken out" totals we see elsewhere in the app.
+ * E.g. on the rleated list by type, the above would show as:
+ * 2 dns
+ * 2 file
+ * 1 registry
+ * So it would be extremely disorienting to show the user a "3" above that as a total.
+ */
+ const totalCount = Object.values(relatedStats.events.byCategory).reduce(
+ (sum, val) => sum + val,
+ 0
+ );
+ const eventsString = i18n.translate(
+ 'xpack.securitySolution.endpoint.resolver.panel.processEventCounts.events',
+ {
+ defaultMessage: 'Events',
+ }
+ );
+ const crumbs = useMemo(() => {
+ return [
+ {
+ text: eventsString,
+ onClick: () => {
+ pushToQueryParams({ crumbId: '', crumbEvent: '' });
+ },
+ },
+ {
+ text: processName,
+ onClick: () => {
+ pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' });
+ },
+ },
+ {
+ text: (
+ <>
+
+ >
+ ),
+ onClick: () => {
+ pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' });
+ },
+ },
+ ];
+ }, [processName, totalCount, processEntityId, pushToQueryParams, eventsString]);
+ const rows = useMemo(() => {
+ return Object.entries(relatedEventsState.stats).map(
+ ([eventType, count]): EventCountsTableView => {
+ return {
+ name: eventType,
+ count,
+ };
+ }
+ );
+ }, [relatedEventsState]);
+ const columns = useMemo>>(
+ () => [
+ {
+ field: 'count',
+ name: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.table.row.count', {
+ defaultMessage: 'Count',
+ }),
+ width: '20%',
+ sortable: true,
+ },
+ {
+ field: 'name',
+ name: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.table.row.eventType', {
+ defaultMessage: 'Event Type',
+ }),
+ width: '80%',
+ sortable: true,
+ render(name: string) {
+ return (
+ {
+ pushToQueryParams({ crumbId: event.entityId(processEvent), crumbEvent: name });
+ }}
+ >
+ {name}
+
+ );
+ },
+ },
+ ],
+ [pushToQueryParams, processEvent]
+ );
+ return (
+ <>
+
+
+ items={rows} columns={columns} sorting />
+ >
+ );
+});
+EventCountsForProcess.displayName = 'EventCountsForProcess';
diff --git a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/index.tsx
similarity index 90%
rename from x-pack/plugins/security_solution/public/resolver/view/panel.tsx
rename to x-pack/plugins/security_solution/public/resolver/view/panels/index.tsx
index f378ab36bac94..7e7e8b757baf7 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/panels/index.tsx
@@ -7,17 +7,17 @@
import React, { memo, useMemo, useContext, useLayoutEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { EuiPanel } from '@elastic/eui';
-import * as selectors from '../store/selectors';
-import { useResolverDispatch } from './use_resolver_dispatch';
-import * as event from '../../../common/endpoint/models/event';
-import { ResolverEvent, ResolverNodeStats } from '../../../common/endpoint/types';
-import { SideEffectContext } from './side_effect_context';
-import { ProcessEventListNarrowedByType } from './panels/panel_content_related_list';
-import { EventCountsForProcess } from './panels/panel_content_related_counts';
-import { ProcessDetails } from './panels/panel_content_process_detail';
-import { ProcessListWithCounts } from './panels/panel_content_process_list';
-import { RelatedEventDetail } from './panels/panel_content_related_detail';
-import { useResolverQueryParams } from './use_resolver_query_params';
+import * as selectors from '../../store/selectors';
+import { useResolverDispatch } from '../use_resolver_dispatch';
+import * as event from '../../../../common/endpoint/models/event';
+import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types';
+import { SideEffectContext } from '../side_effect_context';
+import { ProcessEventList } from './process_event_list';
+import { EventCountsForProcess } from './event_counts_for_process';
+import { ProcessDetails } from './process_details';
+import { ProcessListWithCounts } from './process_list_with_counts';
+import { RelatedEventDetail } from './related_event_detail';
+import { useResolverQueryParams } from '../use_resolver_query_params';
/**
* The team decided to use this table to determine which breadcrumbs/view to display:
@@ -145,7 +145,7 @@ const PanelContent = memo(function PanelContent() {
*/
if (crumbEvent && crumbEvent.length && uiSelectedEvent) {
- return 'processEventListNarrowedByType';
+ return 'processEventList';
}
}
@@ -179,9 +179,9 @@ const PanelContent = memo(function PanelContent() {
);
}
- if (panelToShow === 'processEventListNarrowedByType') {
+ if (panelToShow === 'processEventList') {
return (
- void;
-}
-
-const StyledRelatedLimitWarning = styled(RelatedEventLimitWarning)`
- flex-flow: row wrap;
- display: block;
- align-items: baseline;
- margin-top: 1em;
-
- & .euiCallOutHeader {
- display: inline;
- margin-right: 0.25em;
- }
-
- & .euiText {
- display: inline;
- }
-
- & .euiText p {
- display: inline;
- }
-`;
-
-const DisplayList = memo(function DisplayList({
- crumbs,
- matchingEventEntries,
- eventType,
- processEntityId,
-}: {
- crumbs: Array<{ text: string | JSX.Element; onClick: () => void }>;
- matchingEventEntries: MatchingEventEntry[];
- eventType: string;
- processEntityId: string;
-}) {
- const relatedLookupsByCategory = useSelector(selectors.relatedEventInfoByEntityId);
- const lookupsForThisNode = relatedLookupsByCategory(processEntityId);
- const shouldShowLimitWarning = lookupsForThisNode?.shouldShowLimitForCategory(eventType);
- const numberDisplayed = lookupsForThisNode?.numberActuallyDisplayedForCategory(eventType);
- const numberMissing = lookupsForThisNode?.numberNotDisplayedForCategory(eventType);
-
- return (
- <>
-
- {shouldShowLimitWarning && typeof numberDisplayed !== 'undefined' && numberMissing ? (
-
- ) : null}
-
- <>
- {matchingEventEntries.map((eventView, index) => {
- const { subject, descriptor = '' } = eventView.name;
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- {index === matchingEventEntries.length - 1 ? null : }
-
- );
- })}
- >
- >
- );
-});
-
-export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarrowedByType({
- processEvent,
- eventType,
- relatedStats,
- pushToQueryParams,
-}: {
- processEvent: ResolverEvent;
- pushToQueryParams: (arg0: CrumbInfo) => unknown;
- eventType: string;
- relatedStats: ResolverNodeStats;
-}) {
- const processName = processEvent && event.eventName(processEvent);
- const processEntityId = event.entityId(processEvent);
- const totalCount = Object.values(relatedStats.events.byCategory).reduce(
- (sum, val) => sum + val,
- 0
- );
- const eventsString = i18n.translate(
- 'xpack.securitySolution.endpoint.resolver.panel.processEventListByType.events',
- {
- defaultMessage: 'Events',
- }
- );
- const waitingString = i18n.translate(
- 'xpack.securitySolution.endpoint.resolver.panel.processEventListByType.wait',
- {
- defaultMessage: 'Waiting For Events...',
- }
- );
-
- const relatedsReadyMap = useSelector(selectors.relatedEventsReady);
- const relatedsReady = relatedsReadyMap.get(processEntityId);
-
- const dispatch = useResolverDispatch();
-
- useEffect(() => {
- if (typeof relatedsReady === 'undefined') {
- dispatch({
- type: 'appDetectedMissingEventData',
- payload: processEntityId,
- });
- }
- }, [relatedsReady, dispatch, processEntityId]);
-
- const waitCrumbs = useMemo(() => {
- return [
- {
- text: eventsString,
- onClick: () => {
- pushToQueryParams({ crumbId: '', crumbEvent: '' });
- },
- },
- ];
- }, [pushToQueryParams, eventsString]);
-
- const relatedByCategory = useSelector(selectors.relatedEventsByCategory);
-
- /**
- * A list entry will be displayed for each of these
- */
- const matchingEventEntries: MatchingEventEntry[] = useMemo(() => {
- const relateds = relatedByCategory(processEntityId)(eventType).map((resolverEvent) => {
- const eventTime = event.eventTimestamp(resolverEvent);
- const formattedDate = typeof eventTime === 'undefined' ? '' : formatDate(eventTime);
- const entityId = event.eventId(resolverEvent);
-
- return {
- formattedDate,
- eventCategory: `${eventType}`,
- eventType: `${event.ecsEventType(resolverEvent)}`,
- name: event.descriptiveName(resolverEvent),
- setQueryParams: () => {
- pushToQueryParams({
- crumbId: entityId === undefined ? '' : String(entityId),
- crumbEvent: processEntityId,
- });
- },
- };
- });
- return relateds;
- }, [relatedByCategory, eventType, processEntityId, pushToQueryParams]);
-
- const crumbs = useMemo(() => {
- return [
- {
- text: eventsString,
- onClick: () => {
- pushToQueryParams({ crumbId: '', crumbEvent: '' });
- },
- },
- {
- text: processName,
- onClick: () => {
- pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' });
- },
- },
- {
- text: (
- <>
-
- >
- ),
- onClick: () => {
- pushToQueryParams({ crumbId: processEntityId, crumbEvent: 'all' });
- },
- },
- {
- text: (
- <>
-
- >
- ),
- onClick: () => {},
- },
- ];
- }, [
- eventType,
- eventsString,
- matchingEventEntries.length,
- processEntityId,
- processName,
- pushToQueryParams,
- totalCount,
- ]);
-
- /**
- * Wait here until the effect resolves...
- */
- if (!relatedsReady) {
- return (
- <>
-
-
-
- {waitingString}
-
- >
- );
- }
-
- return (
-
- );
-});
-ProcessEventListNarrowedByType.displayName = 'ProcessEventListNarrowedByType';
+/*
+ * 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 React, { memo, useMemo, useEffect, Fragment } from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiTitle, EuiSpacer, EuiText, EuiButtonEmpty, EuiHorizontalRule } from '@elastic/eui';
+import { useSelector } from 'react-redux';
+import { FormattedMessage } from 'react-intl';
+import styled from 'styled-components';
+import {
+ CrumbInfo,
+ formatDate,
+ StyledBreadcrumbs,
+ BoldCode,
+ StyledTime,
+} from './panel_content_utilities';
+import * as event from '../../../../common/endpoint/models/event';
+import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types';
+import * as selectors from '../../store/selectors';
+import { useResolverDispatch } from '../use_resolver_dispatch';
+import { RelatedEventLimitWarning } from '../limit_warnings';
+
+/**
+ * This view presents a list of related events of a given type for a given process.
+ * It will appear like:
+ *
+ * | |
+ * | :----------------------------------------------------- |
+ * | **registry deletion** @ *3:32PM..* *HKLM/software...* |
+ * | **file creation** @ *3:34PM..* *C:/directory/file.exe* |
+ */
+
+interface MatchingEventEntry {
+ formattedDate: string;
+ eventType: string;
+ eventCategory: string;
+ name: { subject: string; descriptor?: string };
+ setQueryParams: () => void;
+}
+
+const StyledRelatedLimitWarning = styled(RelatedEventLimitWarning)`
+ flex-flow: row wrap;
+ display: block;
+ align-items: baseline;
+ margin-top: 1em;
+
+ & .euiCallOutHeader {
+ display: inline;
+ margin-right: 0.25em;
+ }
+
+ & .euiText {
+ display: inline;
+ }
+
+ & .euiText p {
+ display: inline;
+ }
+`;
+
+const DisplayList = memo(function DisplayList({
+ crumbs,
+ matchingEventEntries,
+ eventType,
+ processEntityId,
+}: {
+ crumbs: Array<{ text: string | JSX.Element; onClick: () => void }>;
+ matchingEventEntries: MatchingEventEntry[];
+ eventType: string;
+ processEntityId: string;
+}) {
+ const relatedLookupsByCategory = useSelector(selectors.relatedEventInfoByEntityId);
+ const lookupsForThisNode = relatedLookupsByCategory(processEntityId);
+ const shouldShowLimitWarning = lookupsForThisNode?.shouldShowLimitForCategory(eventType);
+ const numberDisplayed = lookupsForThisNode?.numberActuallyDisplayedForCategory(eventType);
+ const numberMissing = lookupsForThisNode?.numberNotDisplayedForCategory(eventType);
+
+ return (
+ <>
+
+ {shouldShowLimitWarning && typeof numberDisplayed !== 'undefined' && numberMissing ? (
+
+ ) : null}
+
+ <>
+ {matchingEventEntries.map((eventView, index) => {
+ const { subject, descriptor = '' } = eventView.name;
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {index === matchingEventEntries.length - 1 ? null : }
+
+ );
+ })}
+ >
+ >
+ );
+});
+
+export const ProcessEventList = memo(function ProcessEventList({
+ processEvent,
+ eventType,
+ relatedStats,
+ pushToQueryParams,
+}: {
+ processEvent: ResolverEvent;
+ pushToQueryParams: (arg0: CrumbInfo) => unknown;
+ eventType: string;
+ relatedStats: ResolverNodeStats;
+}) {
+ const processName = processEvent && event.eventName(processEvent);
+ const processEntityId = event.entityId(processEvent);
+ const totalCount = Object.values(relatedStats.events.byCategory).reduce(
+ (sum, val) => sum + val,
+ 0
+ );
+ const eventsString = i18n.translate(
+ 'xpack.securitySolution.endpoint.resolver.panel.processEventListByType.events',
+ {
+ defaultMessage: 'Events',
+ }
+ );
+ const waitingString = i18n.translate(
+ 'xpack.securitySolution.endpoint.resolver.panel.processEventListByType.wait',
+ {
+ defaultMessage: 'Waiting For Events...',
+ }
+ );
+
+ const relatedsReadyMap = useSelector(selectors.relatedEventsReady);
+ const relatedsReady = relatedsReadyMap.get(processEntityId);
+
+ const dispatch = useResolverDispatch();
+
+ useEffect(() => {
+ if (typeof relatedsReady === 'undefined') {
+ dispatch({
+ type: 'appDetectedMissingEventData',
+ payload: processEntityId,
+ });
+ }
+ }, [relatedsReady, dispatch, processEntityId]);
+
+ const waitCrumbs = useMemo(() => {
+ return [
+ {
+ text: eventsString,
+ onClick: () => {
+ pushToQueryParams({ crumbId: '', crumbEvent: '' });
+ },
+ },
+ ];
+ }, [pushToQueryParams, eventsString]);
+
+ const relatedByCategory = useSelector(selectors.relatedEventsByCategory);
+
+ /**
+ * A list entry will be displayed for each of these
+ */
+ const matchingEventEntries: MatchingEventEntry[] = useMemo(() => {
+ const relateds = relatedByCategory(processEntityId)(eventType).map((resolverEvent) => {
+ const eventTime = event.eventTimestamp(resolverEvent);
+ const formattedDate = typeof eventTime === 'undefined' ? '' : formatDate(eventTime);
+ const entityId = event.eventId(resolverEvent);
+
+ return {
+ formattedDate,
+ eventCategory: `${eventType}`,
+ eventType: `${event.ecsEventType(resolverEvent)}`,
+ name: event.descriptiveName(resolverEvent),
+ setQueryParams: () => {
+ pushToQueryParams({
+ crumbId: entityId === undefined ? '' : String(entityId),
+ crumbEvent: processEntityId,
+ });
+ },
+ };
+ });
+ return relateds;
+ }, [relatedByCategory, eventType, processEntityId, pushToQueryParams]);
+
+ const crumbs = useMemo(() => {
+ return [
+ {
+ text: eventsString,
+ onClick: () => {
+ pushToQueryParams({ crumbId: '', crumbEvent: '' });
+ },
+ },
+ {
+ text: processName,
+ onClick: () => {
+ pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' });
+ },
+ },
+ {
+ text: (
+ <>
+
+ >
+ ),
+ onClick: () => {
+ pushToQueryParams({ crumbId: processEntityId, crumbEvent: 'all' });
+ },
+ },
+ {
+ text: (
+ <>
+
+ >
+ ),
+ onClick: () => {},
+ },
+ ];
+ }, [
+ eventType,
+ eventsString,
+ matchingEventEntries.length,
+ processEntityId,
+ processName,
+ pushToQueryParams,
+ totalCount,
+ ]);
+
+ /**
+ * Wait here until the effect resolves...
+ */
+ if (!relatedsReady) {
+ return (
+ <>
+
+
+
+ {waitingString}
+
+ >
+ );
+ }
+
+ return (
+
+ );
+});
+ProcessEventList.displayName = 'ProcessEventList';
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/process_list_with_counts.tsx
similarity index 99%
rename from x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx
rename to x-pack/plugins/security_solution/public/resolver/view/panels/process_list_with_counts.tsx
index 70422a6919e51..046c840470262 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/process_list_with_counts.tsx
@@ -19,7 +19,7 @@ import * as selectors from '../../store/selectors';
import { CrumbInfo, formatter, StyledBreadcrumbs } from './panel_content_utilities';
import { useResolverDispatch } from '../use_resolver_dispatch';
import { SideEffectContext } from '../side_effect_context';
-import { CubeForProcess } from './process_cube_icon';
+import { CubeForProcess } from './cube_for_process';
import { SafeResolverEvent } from '../../../../common/endpoint/types';
import { LimitWarning } from '../limit_warnings';
diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/related_event_detail.tsx
similarity index 97%
rename from x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx
rename to x-pack/plugins/security_solution/public/resolver/view/panels/related_event_detail.tsx
index 10e57a09b5da4..3579b1b2f69b8 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/panels/related_event_detail.tsx
@@ -1,375 +1,375 @@
-/*
- * 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 React, { memo, useMemo, useEffect, Fragment } from 'react';
-import { i18n } from '@kbn/i18n';
-import { EuiSpacer, EuiText, EuiDescriptionList, EuiTextColor, EuiTitle } from '@elastic/eui';
-import styled from 'styled-components';
-import { useSelector } from 'react-redux';
-import { FormattedMessage } from 'react-intl';
-import {
- CrumbInfo,
- formatDate,
- StyledBreadcrumbs,
- BoldCode,
- StyledTime,
-} from './panel_content_utilities';
-import * as event from '../../../../common/endpoint/models/event';
-import { ResolverEvent } from '../../../../common/endpoint/types';
-import * as selectors from '../../store/selectors';
-import { useResolverDispatch } from '../use_resolver_dispatch';
-import { PanelContentError } from './panel_content_error';
-
-/**
- * A helper function to turn objects into EuiDescriptionList entries.
- * This reflects the strategy of more or less "dumping" metadata for related processes
- * in description lists with little/no 'prettification'. This has the obvious drawback of
- * data perhaps appearing inscrutable/daunting, but the benefit of presenting these fields
- * to the user "as they occur" in ECS, which may help them with e.g. EQL queries.
- *
- * Given an object like: {a:{b: 1}, c: 'd'} it will yield title/description entries like so:
- * {title: "a.b", description: "1"}, {title: "c", description: "d"}
- *
- * @param {object} obj The object to turn into `` entries
- */
-const objectToDescriptionListEntries = function* (
- obj: object,
- prefix = ''
-): Generator<{ title: string; description: string }> {
- const nextPrefix = prefix.length ? `${prefix}.` : '';
- for (const [metaKey, metaValue] of Object.entries(obj)) {
- if (typeof metaValue === 'number' || typeof metaValue === 'string') {
- yield { title: nextPrefix + metaKey, description: `${metaValue}` };
- } else if (metaValue instanceof Array) {
- yield {
- title: nextPrefix + metaKey,
- description: metaValue
- .filter((arrayEntry) => {
- return typeof arrayEntry === 'number' || typeof arrayEntry === 'string';
- })
- .join(','),
- };
- } else if (typeof metaValue === 'object') {
- yield* objectToDescriptionListEntries(metaValue, nextPrefix + metaKey);
- }
- }
-};
-
-// Adding some styles to prevent horizontal scrollbars, per request from UX review
-const StyledDescriptionList = memo(styled(EuiDescriptionList)`
- &.euiDescriptionList.euiDescriptionList--column dt.euiDescriptionList__title.desc-title {
- max-width: 8em;
- }
- &.euiDescriptionList.euiDescriptionList--column dd.euiDescriptionList__description {
- max-width: calc(100% - 8.5em);
- overflow-wrap: break-word;
- }
-`);
-
-// Styling subtitles, per UX review:
-const StyledFlexTitle = memo(styled('h3')`
- display: flex;
- flex-flow: row;
- font-size: 1.2em;
-`);
-const StyledTitleRule = memo(styled('hr')`
- &.euiHorizontalRule.euiHorizontalRule--full.euiHorizontalRule--marginSmall.override {
- display: block;
- flex: 1;
- margin-left: 0.5em;
- }
-`);
-
-const TitleHr = memo(() => {
- return (
-
- );
-});
-TitleHr.displayName = 'TitleHR';
-
-/**
- * This view presents a detailed view of all the available data for a related event, split and titled by the "section"
- * it appears in the underlying ResolverEvent
- */
-export const RelatedEventDetail = memo(function RelatedEventDetail({
- relatedEventId,
- parentEvent,
- pushToQueryParams,
- countForParent,
-}: {
- relatedEventId: string;
- parentEvent: ResolverEvent;
- pushToQueryParams: (queryStringKeyValuePair: CrumbInfo) => unknown;
- countForParent: number | undefined;
-}) {
- const processName = (parentEvent && event.eventName(parentEvent)) || '*';
- const processEntityId = parentEvent && event.entityId(parentEvent);
- const totalCount = countForParent || 0;
- const eventsString = i18n.translate(
- 'xpack.securitySolution.endpoint.resolver.panel.relatedEventDetail.events',
- {
- defaultMessage: 'Events',
- }
- );
- const naString = i18n.translate(
- 'xpack.securitySolution.endpoint.resolver.panel.relatedEventDetail.NA',
- {
- defaultMessage: 'N/A',
- }
- );
-
- const relatedsReadyMap = useSelector(selectors.relatedEventsReady);
- const relatedsReady = relatedsReadyMap.get(processEntityId!);
- const dispatch = useResolverDispatch();
-
- /**
- * If we don't have the related events for the parent yet, use this effect
- * to request them.
- */
- useEffect(() => {
- if (typeof relatedsReady === 'undefined') {
- dispatch({
- type: 'appDetectedMissingEventData',
- payload: processEntityId,
- });
- }
- }, [relatedsReady, dispatch, processEntityId]);
-
- const relatedEventsForThisProcess = useSelector(selectors.relatedEventsByEntityId).get(
- processEntityId!
- );
-
- const [relatedEventToShowDetailsFor, countBySameCategory, relatedEventCategory] = useMemo(() => {
- if (!relatedEventsForThisProcess) {
- return [undefined, 0];
- }
- const specificEvent = relatedEventsForThisProcess.events.find(
- (evt) => event.eventId(evt) === relatedEventId
- );
- // For breadcrumbs:
- const specificCategory = specificEvent && event.primaryEventCategory(specificEvent);
- const countOfCategory = relatedEventsForThisProcess.events.reduce((sumtotal, evt) => {
- return event.primaryEventCategory(evt) === specificCategory ? sumtotal + 1 : sumtotal;
- }, 0);
- return [specificEvent, countOfCategory, specificCategory || naString];
- }, [relatedEventsForThisProcess, naString, relatedEventId]);
-
- const [sections, formattedDate] = useMemo(() => {
- if (!relatedEventToShowDetailsFor) {
- // This could happen if user relaods from URL param and requests an eventId that no longer exists
- return [[], naString];
- }
- // Assuming these details (agent, ecs, process) aren't as helpful, can revisit
- const {
- agent,
- ecs,
- process,
- ...relevantData
- } = relatedEventToShowDetailsFor as ResolverEvent & {
- // Type this with various unknown keys so that ts will let us delete those keys
- ecs: unknown;
- process: unknown;
- };
- let displayDate = '';
- const sectionData: Array<{
- sectionTitle: string;
- entries: Array<{ title: string; description: string }>;
- }> = Object.entries(relevantData)
- .map(([sectionTitle, val]) => {
- if (sectionTitle === '@timestamp') {
- displayDate = formatDate(val);
- return { sectionTitle: '', entries: [] };
- }
- if (typeof val !== 'object') {
- return { sectionTitle, entries: [{ title: sectionTitle, description: `${val}` }] };
- }
- return { sectionTitle, entries: [...objectToDescriptionListEntries(val)] };
- })
- .filter((v) => v.sectionTitle !== '' && v.entries.length);
- return [sectionData, displayDate];
- }, [relatedEventToShowDetailsFor, naString]);
-
- const waitCrumbs = useMemo(() => {
- return [
- {
- text: eventsString,
- onClick: () => {
- pushToQueryParams({ crumbId: '', crumbEvent: '' });
- },
- },
- ];
- }, [pushToQueryParams, eventsString]);
-
- const { subject = '', descriptor = '' } = relatedEventToShowDetailsFor
- ? event.descriptiveName(relatedEventToShowDetailsFor)
- : {};
- const crumbs = useMemo(() => {
- return [
- {
- text: eventsString,
- onClick: () => {
- pushToQueryParams({ crumbId: '', crumbEvent: '' });
- },
- },
- {
- text: processName,
- onClick: () => {
- pushToQueryParams({ crumbId: processEntityId!, crumbEvent: '' });
- },
- },
- {
- text: (
- <>
-
- >
- ),
- onClick: () => {
- pushToQueryParams({ crumbId: processEntityId!, crumbEvent: 'all' });
- },
- },
- {
- text: (
- <>
-
- >
- ),
- onClick: () => {
- pushToQueryParams({
- crumbId: processEntityId!,
- crumbEvent: relatedEventCategory || 'all',
- });
- },
- },
- {
- text: relatedEventToShowDetailsFor ? (
-
- ) : (
- naString
- ),
- onClick: () => {},
- },
- ];
- }, [
- processName,
- processEntityId,
- eventsString,
- pushToQueryParams,
- totalCount,
- countBySameCategory,
- naString,
- relatedEventCategory,
- relatedEventToShowDetailsFor,
- subject,
- descriptor,
- ]);
-
- /**
- * If the ship hasn't come in yet, wait on the dock
- */
- if (!relatedsReady) {
- const waitingString = i18n.translate(
- 'xpack.securitySolution.endpoint.resolver.panel.relatedDetail.wait',
- {
- defaultMessage: 'Waiting For Events...',
- }
- );
- return (
- <>
-
-
-
- {waitingString}
-
- >
- );
- }
-
- /**
- * Could happen if user e.g. loads a URL with a bad crumbEvent
- */
- if (!relatedEventToShowDetailsFor) {
- const errString = i18n.translate(
- 'xpack.securitySolution.endpoint.resolver.panel.relatedDetail.missing',
- {
- defaultMessage: 'Related event not found.',
- }
- );
- return (
-
- );
- }
-
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {sections.map(({ sectionTitle, entries }, index) => {
- return (
-
- {index === 0 ? null : }
-
-
-
- {sectionTitle}
-
-
-
-
-
-
- {index === sections.length - 1 ? null : }
-
- );
- })}
- >
- );
-});
+/*
+ * 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 React, { memo, useMemo, useEffect, Fragment } from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiSpacer, EuiText, EuiDescriptionList, EuiTextColor, EuiTitle } from '@elastic/eui';
+import styled from 'styled-components';
+import { useSelector } from 'react-redux';
+import { FormattedMessage } from 'react-intl';
+import {
+ CrumbInfo,
+ formatDate,
+ StyledBreadcrumbs,
+ BoldCode,
+ StyledTime,
+} from './panel_content_utilities';
+import * as event from '../../../../common/endpoint/models/event';
+import { ResolverEvent } from '../../../../common/endpoint/types';
+import * as selectors from '../../store/selectors';
+import { useResolverDispatch } from '../use_resolver_dispatch';
+import { PanelContentError } from './panel_content_error';
+
+/**
+ * A helper function to turn objects into EuiDescriptionList entries.
+ * This reflects the strategy of more or less "dumping" metadata for related processes
+ * in description lists with little/no 'prettification'. This has the obvious drawback of
+ * data perhaps appearing inscrutable/daunting, but the benefit of presenting these fields
+ * to the user "as they occur" in ECS, which may help them with e.g. EQL queries.
+ *
+ * Given an object like: {a:{b: 1}, c: 'd'} it will yield title/description entries like so:
+ * {title: "a.b", description: "1"}, {title: "c", description: "d"}
+ *
+ * @param {object} obj The object to turn into ` ` entries
+ */
+const objectToDescriptionListEntries = function* (
+ obj: object,
+ prefix = ''
+): Generator<{ title: string; description: string }> {
+ const nextPrefix = prefix.length ? `${prefix}.` : '';
+ for (const [metaKey, metaValue] of Object.entries(obj)) {
+ if (typeof metaValue === 'number' || typeof metaValue === 'string') {
+ yield { title: nextPrefix + metaKey, description: `${metaValue}` };
+ } else if (metaValue instanceof Array) {
+ yield {
+ title: nextPrefix + metaKey,
+ description: metaValue
+ .filter((arrayEntry) => {
+ return typeof arrayEntry === 'number' || typeof arrayEntry === 'string';
+ })
+ .join(','),
+ };
+ } else if (typeof metaValue === 'object') {
+ yield* objectToDescriptionListEntries(metaValue, nextPrefix + metaKey);
+ }
+ }
+};
+
+// Adding some styles to prevent horizontal scrollbars, per request from UX review
+const StyledDescriptionList = memo(styled(EuiDescriptionList)`
+ &.euiDescriptionList.euiDescriptionList--column dt.euiDescriptionList__title.desc-title {
+ max-width: 8em;
+ }
+ &.euiDescriptionList.euiDescriptionList--column dd.euiDescriptionList__description {
+ max-width: calc(100% - 8.5em);
+ overflow-wrap: break-word;
+ }
+`);
+
+// Styling subtitles, per UX review:
+const StyledFlexTitle = memo(styled('h3')`
+ display: flex;
+ flex-flow: row;
+ font-size: 1.2em;
+`);
+const StyledTitleRule = memo(styled('hr')`
+ &.euiHorizontalRule.euiHorizontalRule--full.euiHorizontalRule--marginSmall.override {
+ display: block;
+ flex: 1;
+ margin-left: 0.5em;
+ }
+`);
+
+const TitleHr = memo(() => {
+ return (
+
+ );
+});
+TitleHr.displayName = 'TitleHR';
+
+/**
+ * This view presents a detailed view of all the available data for a related event, split and titled by the "section"
+ * it appears in the underlying ResolverEvent
+ */
+export const RelatedEventDetail = memo(function RelatedEventDetail({
+ relatedEventId,
+ parentEvent,
+ pushToQueryParams,
+ countForParent,
+}: {
+ relatedEventId: string;
+ parentEvent: ResolverEvent;
+ pushToQueryParams: (queryStringKeyValuePair: CrumbInfo) => unknown;
+ countForParent: number | undefined;
+}) {
+ const processName = (parentEvent && event.eventName(parentEvent)) || '*';
+ const processEntityId = parentEvent && event.entityId(parentEvent);
+ const totalCount = countForParent || 0;
+ const eventsString = i18n.translate(
+ 'xpack.securitySolution.endpoint.resolver.panel.relatedEventDetail.events',
+ {
+ defaultMessage: 'Events',
+ }
+ );
+ const naString = i18n.translate(
+ 'xpack.securitySolution.endpoint.resolver.panel.relatedEventDetail.NA',
+ {
+ defaultMessage: 'N/A',
+ }
+ );
+
+ const relatedsReadyMap = useSelector(selectors.relatedEventsReady);
+ const relatedsReady = relatedsReadyMap.get(processEntityId!);
+ const dispatch = useResolverDispatch();
+
+ /**
+ * If we don't have the related events for the parent yet, use this effect
+ * to request them.
+ */
+ useEffect(() => {
+ if (typeof relatedsReady === 'undefined') {
+ dispatch({
+ type: 'appDetectedMissingEventData',
+ payload: processEntityId,
+ });
+ }
+ }, [relatedsReady, dispatch, processEntityId]);
+
+ const relatedEventsForThisProcess = useSelector(selectors.relatedEventsByEntityId).get(
+ processEntityId!
+ );
+
+ const [relatedEventToShowDetailsFor, countBySameCategory, relatedEventCategory] = useMemo(() => {
+ if (!relatedEventsForThisProcess) {
+ return [undefined, 0];
+ }
+ const specificEvent = relatedEventsForThisProcess.events.find(
+ (evt) => event.eventId(evt) === relatedEventId
+ );
+ // For breadcrumbs:
+ const specificCategory = specificEvent && event.primaryEventCategory(specificEvent);
+ const countOfCategory = relatedEventsForThisProcess.events.reduce((sumtotal, evt) => {
+ return event.primaryEventCategory(evt) === specificCategory ? sumtotal + 1 : sumtotal;
+ }, 0);
+ return [specificEvent, countOfCategory, specificCategory || naString];
+ }, [relatedEventsForThisProcess, naString, relatedEventId]);
+
+ const [sections, formattedDate] = useMemo(() => {
+ if (!relatedEventToShowDetailsFor) {
+ // This could happen if user relaods from URL param and requests an eventId that no longer exists
+ return [[], naString];
+ }
+ // Assuming these details (agent, ecs, process) aren't as helpful, can revisit
+ const {
+ agent,
+ ecs,
+ process,
+ ...relevantData
+ } = relatedEventToShowDetailsFor as ResolverEvent & {
+ // Type this with various unknown keys so that ts will let us delete those keys
+ ecs: unknown;
+ process: unknown;
+ };
+ let displayDate = '';
+ const sectionData: Array<{
+ sectionTitle: string;
+ entries: Array<{ title: string; description: string }>;
+ }> = Object.entries(relevantData)
+ .map(([sectionTitle, val]) => {
+ if (sectionTitle === '@timestamp') {
+ displayDate = formatDate(val);
+ return { sectionTitle: '', entries: [] };
+ }
+ if (typeof val !== 'object') {
+ return { sectionTitle, entries: [{ title: sectionTitle, description: `${val}` }] };
+ }
+ return { sectionTitle, entries: [...objectToDescriptionListEntries(val)] };
+ })
+ .filter((v) => v.sectionTitle !== '' && v.entries.length);
+ return [sectionData, displayDate];
+ }, [relatedEventToShowDetailsFor, naString]);
+
+ const waitCrumbs = useMemo(() => {
+ return [
+ {
+ text: eventsString,
+ onClick: () => {
+ pushToQueryParams({ crumbId: '', crumbEvent: '' });
+ },
+ },
+ ];
+ }, [pushToQueryParams, eventsString]);
+
+ const { subject = '', descriptor = '' } = relatedEventToShowDetailsFor
+ ? event.descriptiveName(relatedEventToShowDetailsFor)
+ : {};
+ const crumbs = useMemo(() => {
+ return [
+ {
+ text: eventsString,
+ onClick: () => {
+ pushToQueryParams({ crumbId: '', crumbEvent: '' });
+ },
+ },
+ {
+ text: processName,
+ onClick: () => {
+ pushToQueryParams({ crumbId: processEntityId!, crumbEvent: '' });
+ },
+ },
+ {
+ text: (
+ <>
+
+ >
+ ),
+ onClick: () => {
+ pushToQueryParams({ crumbId: processEntityId!, crumbEvent: 'all' });
+ },
+ },
+ {
+ text: (
+ <>
+
+ >
+ ),
+ onClick: () => {
+ pushToQueryParams({
+ crumbId: processEntityId!,
+ crumbEvent: relatedEventCategory || 'all',
+ });
+ },
+ },
+ {
+ text: relatedEventToShowDetailsFor ? (
+
+ ) : (
+ naString
+ ),
+ onClick: () => {},
+ },
+ ];
+ }, [
+ processName,
+ processEntityId,
+ eventsString,
+ pushToQueryParams,
+ totalCount,
+ countBySameCategory,
+ naString,
+ relatedEventCategory,
+ relatedEventToShowDetailsFor,
+ subject,
+ descriptor,
+ ]);
+
+ /**
+ * If the ship hasn't come in yet, wait on the dock
+ */
+ if (!relatedsReady) {
+ const waitingString = i18n.translate(
+ 'xpack.securitySolution.endpoint.resolver.panel.relatedDetail.wait',
+ {
+ defaultMessage: 'Waiting For Events...',
+ }
+ );
+ return (
+ <>
+
+
+
+ {waitingString}
+
+ >
+ );
+ }
+
+ /**
+ * Could happen if user e.g. loads a URL with a bad crumbEvent
+ */
+ if (!relatedEventToShowDetailsFor) {
+ const errString = i18n.translate(
+ 'xpack.securitySolution.endpoint.resolver.panel.relatedDetail.missing',
+ {
+ defaultMessage: 'Related event not found.',
+ }
+ );
+ return (
+
+ );
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {sections.map(({ sectionTitle, entries }, index) => {
+ return (
+
+ {index === 0 ? null : }
+
+
+
+ {sectionTitle}
+
+
+
+
+
+
+ {index === sections.length - 1 ? null : }
+
+ );
+ })}
+ >
+ );
+});
diff --git a/x-pack/plugins/security_solution/public/resolver/view/styles.tsx b/x-pack/plugins/security_solution/public/resolver/view/styles.tsx
index 4cdb29b283f1e..dfc2f970f1e6f 100644
--- a/x-pack/plugins/security_solution/public/resolver/view/styles.tsx
+++ b/x-pack/plugins/security_solution/public/resolver/view/styles.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import styled from 'styled-components';
-import { Panel } from './panel';
+import { Panel } from './panels';
/**
* The top level DOM element for Resolver