From 39b3aa6f463e70bcdb79fbee126c9ba29ee67fb5 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Fri, 12 Jun 2020 17:11:43 -0400 Subject: [PATCH 01/78] linted rebase --- .../common/endpoint/generate_data.ts | 54 ++- .../common/endpoint/models/event.ts | 97 ++++ .../common/endpoint/types.ts | 15 + .../endpoint_alerts/store/middleware.ts | 6 +- .../public/endpoint_alerts/store/selectors.ts | 22 + .../public/resolver/models/process_event.ts | 68 +++ .../public/resolver/store/actions.ts | 6 +- .../public/resolver/store/reducer.ts | 12 +- .../public/resolver/store/selectors.ts | 16 + .../public/resolver/store/ui/selectors.ts | 11 + .../public/resolver/types.ts | 4 + .../public/resolver/view/index.tsx | 13 +- .../public/resolver/view/panel.tsx | 430 +++++++++++------- .../view/panels/panel_content_error.tsx | 61 +++ .../panels/panel_content_process_detail.tsx | 147 ++++++ .../panels/panel_content_process_list.tsx | 155 +++++++ .../panels/panel_content_related_counts.tsx | 134 ++++++ .../panels/panel_content_related_detail.tsx | 246 ++++++++++ .../panels/panel_content_related_list.tsx | 142 ++++++ .../view/panels/panel_content_wait.tsx | 59 +++ .../view/panels/process_cube_icon.tsx | 50 ++ .../resolver/view/process_event_dot.tsx | 390 ++++++++-------- .../public/resolver/view/submenu.tsx | 53 ++- 23 files changed, 1816 insertions(+), 375 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx create mode 100644 x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_detail.tsx create mode 100644 x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx create mode 100644 x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_counts.tsx create mode 100644 x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx create mode 100644 x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx create mode 100644 x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_wait.tsx create mode 100644 x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index 2d004d3315beb..6e698f240db1a 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -26,6 +26,9 @@ interface EventOptions { eventType?: string; eventCategory?: string | string[]; processName?: string; + pid?: number; + parentPid?: number; + extensions?: object; } const Windows: HostOS[] = [ @@ -446,12 +449,36 @@ export class EndpointDocGenerator { * @param options - Allows event field values to be specified */ public generateEvent(options: EventOptions = {}): EndpointEvent { + const processName = options.processName ? options.processName : randomProcessName(); + const detailRecordForEventType = + options.extensions || + ((eventCategory) => { + if (eventCategory === 'registry') { + return { registry: { key: `HKLM/Windows/Software/${this.randomString(5)}` } }; + } + if (eventCategory === 'network') { + return { + network: { + direction: this.randomChoice(['inbound', 'outbound']), + forwarded_ip: `${this.randomIP()}`, + }, + }; + } + if (eventCategory === 'file') { + return { file: { path: 'C:\\My Documents\\business\\January\\processName' } }; + } + if (eventCategory === 'dns') { + return { dns: { question: { name: `${this.randomIP()}` } } }; + } + return {}; + })(options.eventCategory); return { '@timestamp': options.timestamp ? options.timestamp : new Date().getTime(), agent: { ...this.commonInfo.agent, type: 'endpoint' }, ecs: { version: '1.4.0', }, + ...detailRecordForEventType, event: { category: options.eventCategory ? options.eventCategory : 'process', kind: 'event', @@ -460,9 +487,26 @@ export class EndpointDocGenerator { }, host: this.commonInfo.host, process: { + pid: options.pid ? options.pid : this.randomN(5000), + executable: `C:\\${processName}`, + args: `"C:\\${processName}" \\${this.randomString(3)}`, + code_signature: { + status: 'trusted', + subject_name: 'Microsoft', + }, + hash: { md5: this.seededUUIDv4() }, entity_id: options.entityID ? options.entityID : this.randomString(10), - parent: options.parentEntityID ? { entity_id: options.parentEntityID } : undefined, - name: options.processName ? options.processName : randomProcessName(), + parent: options.parentEntityID + ? { + entity_id: options.parentEntityID, + pid: options.parentPid ? options.parentPid : this.randomN(5000), + } + : undefined, + name: processName, + }, + user: { + domain: this.randomString(10), + name: this.randomString(10), }, }; } @@ -633,7 +677,7 @@ export class EndpointDocGenerator { ): Event[] { const events = []; const startDate = new Date().getTime(); - const root = this.generateEvent({ timestamp: startDate + 1000 }); + const root = this.generateEvent({ timestamp: startDate + 1000, pid: this.randomN(5000) }); events.push(root); let ancestor = root; let timestamp = root['@timestamp'] + 1000; @@ -668,6 +712,8 @@ export class EndpointDocGenerator { ancestor = this.generateEvent({ timestamp, parentEntityID: ancestor.process.entity_id, + parentPid: ancestor.process.pid, + pid: this.randomN(5000), }); events.push(ancestor); timestamp = timestamp + 1000; @@ -1072,7 +1118,7 @@ export class EndpointDocGenerator { return [...this.randomNGenerator(255, 6)].map((x) => x.toString(16)).join('-'); } - private randomIP(): string { + public randomIP(): string { return [10, ...this.randomNGenerator(255, 3)].map((x) => x.toString()).join('.'); } diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index 2c325d64f8515..01a2153c8d099 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -72,3 +72,100 @@ export function eventType(event: ResolverEvent): string { } return eventCategoryToReturn; } + +/** + * ECS event type will be things like 'creation', 'deletion', 'access', etc. + * see: https://www.elastic.co/guide/en/ecs/current/ecs-event.html + * @param event The ResolverEvent to get the ecs type for + */ +export function ecsEventType(event: ResolverEvent): string { + if (isLegacyEvent(event)) { + return event.endgame.event_subtype_full || ''; + } + return typeof event.event.type === 'string' ? event.event.type : event.event.type.join('/'); +} + +/** + * #Descriptive Names For Related Events: + * + * The following section provides facilities for deriving **Descriptive Names** for ECS-compliant event data. + * There are drawbacks to trying to do this: It *will* require ongoing maintenance. It presents temptations to overarticulate. + * On balance, however, it seems that the benefit of giving the user some form of information they can recognize & scan outweighs the drawbacks. + */ + +/** + * A type that annotates the basic requirements for giving the event a `descriptive name` + */ +type detailRecord = T extends 'registry' + ? { path: unknown; key: unknown } + : T extends 'dns' + ? { question: { name: unknown } } + : T extends 'network' + ? { direction: unknown; forwarded_ip: unknown } + : T extends 'file' + ? { path: unknown } + : unknown; +type ecsRecordWithType = Record>; + +/** + * Verify that the `ecsCategory` exists as a key on the event record. i.e. if ecsCategory is `registry`, the event is shaped like {registry: object} + * + * @param event + * @param ecsCategory + */ +export function isRecordNamable( + event: object, + ecsCategory: T +): event is ecsRecordWithType { + // The goal here is to reach in and grab a better name for the event (if one exists) without being too obtrusive/assertive about ECS + return typeof (event as ecsRecordWithType)[ecsCategory] === 'object'; +} + +/** + * Based on the ECS category of the event, attempt to provide a more descriptive name + * (e.g. the `event.registry.key` for `registry` or the `dns.question.name` for `dns`, etc.). + * see: https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html + * @param event The ResolverEvent to get the descriptive name for + * @returns { descriptiveName } An attempt at providing a readable name to the user + */ +export function descriptiveName(event: ResolverEvent): string { + if (isLegacyEvent(event)) { + return eventName(event); + } + // For the purposes of providing a descriptive name, we're taking the first entry in the `event.type` + const ecsCategory = eventType(event); + + /** + * This list of attempts can be expanded/adjusted as the underlying model changes over time: + */ + + // Stable, per ECS 1.5: https://www.elastic.co/guide/en/ecs/current/ecs-allowed-values-event-category.html + if (ecsCategory === 'network' && isRecordNamable(event, ecsCategory)) { + if (event?.network?.forwarded_ip) { + return `${event?.network?.direction ? `${event?.network?.direction} ` : ''}${ + event.network.forwarded_ip + }`; + } + } + if (ecsCategory === 'file' && isRecordNamable(event, ecsCategory)) { + if (event?.file?.path) { + return `${event.file.path}`; + } + } + + // Extended categories (per ECS 1.5): + if (ecsCategory === 'registry' && isRecordNamable(event, ecsCategory)) { + const pathOrKey = event?.registry?.path || event?.registry?.key; + if (pathOrKey) { + return `${pathOrKey}`; + } + } + if (ecsCategory === 'dns' && isRecordNamable(event, ecsCategory)) { + if (event?.dns?.question?.name) { + return `${event.dns.question.name}`; + } + } + + // Fall back on entityId if we can't fish a more descriptive name out. + return entityId(event); +} diff --git a/x-pack/plugins/security_solution/common/endpoint/types.ts b/x-pack/plugins/security_solution/common/endpoint/types.ts index cfbf8f176b32d..90e2f841f4ade 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types.ts @@ -433,11 +433,26 @@ export interface EndpointEvent { process: { entity_id: string; name: string; + executable: string; + args: string; + code_signature: { + status: string; + subject_name: string; + }; + pid: number; + hash: { + md5: string; + }; parent?: { entity_id: string; name?: string; + pid: number; }; }; + user: { + domain: string; + name: string; + }; } export type ResolverEvent = EndpointEvent | LegacyEndpointEvent; diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts b/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts index dd84b4fcff5bd..19bf516ce85ca 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts @@ -13,11 +13,13 @@ import { import { AlertConstants } from '../../../common/endpoint_alerts/alert_constants'; import { ImmutableMiddlewareFactory } from '../../common/store'; import { cloneHttpFetchQuery } from '../../common/utils/clone_http_fetch_query'; + import { isOnAlertPage, apiQueryParams, - hasSelectedAlert, uiQueryParams, + selectedAlertHasChanged, + hasSelectedAlert, isAlertPageTabChange, } from './selectors'; @@ -53,7 +55,7 @@ export const alertMiddlewareFactory: ImmutableMiddlewareFactory }); api.dispatch({ type: 'serverReturnedAlertsData', payload: listResponse }); - if (hasSelectedAlert(state)) { + if (hasSelectedAlert(state) && selectedAlertHasChanged(state)) { const uiParams = uiQueryParams(state); const detailsResponse: AlertDetails = await coreStart.http.get( `/api/endpoint/alerts/${uiParams.selected_alert}` diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/store/selectors.ts b/x-pack/plugins/security_solution/public/endpoint_alerts/store/selectors.ts index ab0e4165a2577..c89e08edd4040 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/store/selectors.ts @@ -188,3 +188,25 @@ export const selectedAlertDetailsTabId: ( uiQueryParams, ({ active_details_tab: activeDetailsTab }) => activeDetailsTab ); + +let lastSelectedAlert: string | null = null; +/** + * @returns true once per change of `selectedAlert` in query params. + * + * As opposed to `hasSelectedAlert` which always returns true if the alert is present + * query params, which can cause unnecessary requests and re-renders in some cases. + */ +export const selectedAlertHasChanged: ( + state: Immutable +) => boolean = createSelector(uiQueryParams, function hasChanged({ + selected_alert: selectedAlert, +}) { + if (typeof selectedAlert !== 'string') { + return false; + } + if (selectedAlert === lastSelectedAlert) { + return false; + } + lastSelectedAlert = selectedAlert; + return true; +}); diff --git a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts index 038e5b90b2170..9938c5766d607 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts @@ -78,6 +78,17 @@ export function uniquePidForProcess(passedEvent: ResolverEvent): string { } } +/** + * Returns the pid for the process on the host + */ +export function hostPidForProcess(passedEvent: ResolverEvent): string { + if (event.isLegacyEvent(passedEvent)) { + return String(passedEvent.endgame.unique_pid); + } else { + return String(passedEvent.process.pid); + } +} + /** * Returns the process event's parent pid */ @@ -88,3 +99,60 @@ export function uniqueParentPidForProcess(passedEvent: ResolverEvent): string | return passedEvent.process.parent?.entity_id; } } + +/** + * Returns the process event's parent pid + */ +export function hostParentPidForProcess(passedEvent: ResolverEvent): string | undefined { + if (event.isLegacyEvent(passedEvent)) { + return String(passedEvent.endgame.unique_ppid); + } else { + const ppid = passedEvent.process.parent?.pid; + return ppid ? String(ppid) : undefined; + } +} + +/** + * Returns the process event's path on its host + */ +export function hostPathForProcess(passedEvent: ResolverEvent): string | undefined { + if (event.isLegacyEvent(passedEvent)) { + return passedEvent.endgame.process_path; + } else { + return passedEvent.process.executable; + } +} + +/** + * Returns the username for the account that ran the process + */ +export function userInfoForProcess(passedEvent: ResolverEvent): object | undefined { + return passedEvent.user; +} + +/** + * Returns the MD5 hash for the `passedEvent` param, or undefined if it can't be located + * @param {ResolverEvent} passedEvent The `ResolverEvent` to get the MD5 value for + * @returns {string | undefined} The MD5 string for the event + */ +export function md5HashForProcess(passedEvent: ResolverEvent): string | undefined { + if (event.isLegacyEvent(passedEvent)) { + // There is not currently a key for this on Legacy event types + return undefined; + } + return passedEvent?.process?.hash?.md5; +} + +/** + * Returns the command line path and arguments used to run the `passedEvent` if any + * + * @param {ResolverEvent} passedEvent The `ResolverEvent` to get the arguemnts value for + * @returns {string | undefined} The arguments (including the path) used to run the process + */ +export function argsForProcess(passedEvent: ResolverEvent): string | undefined { + if (event.isLegacyEvent(passedEvent)) { + // There is not currently a key for this on Legacy event types + return undefined; + } + return passedEvent?.process?.args; +} diff --git a/x-pack/plugins/security_solution/public/resolver/store/actions.ts b/x-pack/plugins/security_solution/public/resolver/store/actions.ts index 0963118ce14b8..e1763f6593d04 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/actions.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/actions.ts @@ -80,9 +80,13 @@ interface UserSelectedResolverNode { readonly type: 'userSelectedResolverNode'; readonly payload: { /** - * Used to identify the process node that the user selected + * The HTML ID used to identify the process node's element that the user selected */ readonly nodeId: string; + /** + * The process entity_id for the process the node represents + */ + readonly selectedProcessId: string; }; } diff --git a/x-pack/plugins/security_solution/public/resolver/store/reducer.ts b/x-pack/plugins/security_solution/public/resolver/store/reducer.ts index 82206d77f8349..c7376af6ce896 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/reducer.ts @@ -19,7 +19,11 @@ import { uniquePidForProcess } from '../models/process_event'; const resolverNodeIdGenerator = htmlIdGenerator('resolverNode'); const uiReducer: Reducer = ( - uiState = { activeDescendantId: null, selectedDescendantId: null }, + uiState = { + activeDescendantId: null, + selectedDescendantId: null, + processEntityIdOfSelectedDescendant: null, + }, action ) => { if (action.type === 'userFocusedOnResolverNode') { @@ -31,6 +35,7 @@ const uiReducer: Reducer = ( return { ...uiState, selectedDescendantId: action.payload.nodeId, + processEntityIdOfSelectedDescendant: action.payload.selectedProcessId, }; } else if (action.type === 'userBroughtProcessIntoView') { /** @@ -38,10 +43,13 @@ const uiReducer: Reducer = ( * `uniquePidForProcess` and `resolverNodeIdGenerator` to resolve the determinant * html id of the node being brought into view. */ - const processNodeId = resolverNodeIdGenerator(uniquePidForProcess(action.payload.process)); + const processEntityId = uniquePidForProcess(action.payload.process); + const processNodeId = resolverNodeIdGenerator(processEntityId); return { ...uiState, activeDescendantId: processNodeId, + selectedDescendantId: processNodeId, + processEntityIdOfSelectedDescendant: processEntityId, }; } else { return uiState; 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 82b722bb986ef..81af8eef00355 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/selectors.ts @@ -81,6 +81,14 @@ export const uiSelectedDescendantId = composeSelectors( uiSelectors.selectedDescendantId ); +/** + * Returns the entity_id of the "selected" tree node's process + */ +export const uiSelectedDescendantProcessId = composeSelectors( + uiStateSelector, + uiSelectors.selectedDescendantProcessId +); + /** * Returns the camera state from within ResolverState */ @@ -112,6 +120,14 @@ export const isLoading = composeSelectors(dataStateSelector, dataSelectors.isLoa */ export const hasError = composeSelectors(dataStateSelector, dataSelectors.hasError); +/** + * An array containing all the processes currently in the Resolver than can be graphed + */ +export const graphableProcesses = composeSelectors( + dataStateSelector, + dataSelectors.graphableProcesses +); + /** * Calls the `secondSelector` with the result of the `selector`. Use this when re-exporting a * concern-specific selector. `selector` should return the concern-specific state. diff --git a/x-pack/plugins/security_solution/public/resolver/store/ui/selectors.ts b/x-pack/plugins/security_solution/public/resolver/store/ui/selectors.ts index 196e834c406b3..494d8884329c6 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/ui/selectors.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/ui/selectors.ts @@ -28,3 +28,14 @@ export const selectedDescendantId = createSelector( return selectedDescendantId; } ); + +/** + * id of the currently "selected" tree node + */ +export const selectedDescendantProcessId = createSelector( + (uiState: ResolverUIState) => uiState, + /* eslint-disable no-shadow */ + ({ processEntityIdOfSelectedDescendant }: ResolverUIState) => { + return processEntityIdOfSelectedDescendant; + } +); diff --git a/x-pack/plugins/security_solution/public/resolver/types.ts b/x-pack/plugins/security_solution/public/resolver/types.ts index e93a7856557cc..6c8be94d2d88e 100644 --- a/x-pack/plugins/security_solution/public/resolver/types.ts +++ b/x-pack/plugins/security_solution/public/resolver/types.ts @@ -43,6 +43,10 @@ export interface ResolverUIState { * The ID attribute of the resolver's currently selected descendant. */ readonly selectedDescendantId: string | null; + /** + * The entity_id of the process for the resolver's currently selected descendant. + */ + readonly processEntityIdOfSelectedDescendant: string | null; } /** diff --git a/x-pack/plugins/security_solution/public/resolver/view/index.tsx b/x-pack/plugins/security_solution/public/resolver/view/index.tsx index cae5ba97f413c..fbc6bc8f42284 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/index.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/index.tsx @@ -21,18 +21,23 @@ import { ResolverEvent } from '../../../common/endpoint/types'; const StyledPanel = styled(Panel)` position: absolute; - left: 1em; - top: 1em; - max-height: calc(100% - 2em); + left: 0; + top: 0; + bottom: 0; overflow: auto; width: 25em; max-width: 50%; + border-radius: 0; `; const StyledResolverContainer = styled.div` display: flex; flex-grow: 1; - contain: layout; + contain: strict; + cursor: grab; + &:active { + cursor: grabbing; + } `; const bgColor = NamedColors.resolverBackground; 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 2b67a4ac16d4e..feea377d2d991 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -3,191 +3,301 @@ * 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, useCallback, useMemo, useContext } from 'react'; -import { - EuiPanel, - EuiBadge, - EuiBasicTableColumn, - EuiTitle, - EuiHorizontalRule, - EuiInMemoryTable, -} from '@elastic/eui'; -import euiVars from '@elastic/eui/dist/eui_theme_light.json'; + +import React, { memo, useCallback, useMemo, useContext, useLayoutEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { i18n } from '@kbn/i18n'; -import { SideEffectContext } from './side_effect_context'; -import { ResolverEvent } from '../../../common/endpoint/types'; -import * as event from '../../../common/endpoint/models/event'; -import { useResolverDispatch } from './use_resolver_dispatch'; +import { useHistory } from 'react-router-dom'; +// eslint-disable-next-line import/no-nodejs-modules +import querystring from 'querystring'; + +import { EuiPanel, EuiBreadcrumbs } from '@elastic/eui'; +import styled from 'styled-components'; +import { displayNameRecord } from './process_event_dot'; import * as selectors from '../store/selectors'; +import { useResolverDispatch } from './use_resolver_dispatch'; +import * as event from '../../../common/endpoint/models/event'; +import { ResolverEvent } from '../../../common/endpoint/types'; +import { SideEffectContext } from './side_effect_context'; +import { TableServiceError } from './panels/panel_content_error'; +import { WaitForRelatedEvents } from './panels/panel_content_wait'; +import { RelatedEventDetail } from './panels/panel_content_related_detail'; +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'; -const HorizontalRule = memo(function HorizontalRule() { - return ( - - ); +/** + * The two query parameters we read/write on to control which view the table presents: + */ +export interface CrumbInfo { + readonly crumbId: string; + readonly crumbEvent: string; +} + +/** + * Breadcrumb menu with adjustments per direction from UX team + */ +export const StyledBreadcrumbs = styled(EuiBreadcrumbs)` + &.euiBreadcrumbs.euiBreadcrumbs--responsive { + background-color: #f5f5fa; + padding: 1em; + } +`; + +export const formatter = new Intl.DateTimeFormat(i18n.getLocale(), { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', }); -export const Panel = memo(function Event({ className }: { className?: string }) { - interface ProcessTableView { - name: string; - timestamp?: Date; - event: ResolverEvent; +/** + * @param {ConstructorParameters[0]} timestamp To be passed through Date->Intl.DateTimeFormat + * @returns {string} A nicely formatted string for a date + */ +export function formatDate(timestamp: ConstructorParameters[0]) { + const date = new Date(timestamp); + if (isFinite(date.getTime())) { + return formatter.format(date); + } else { + return 'Invalid Date'; } +} - const { processNodePositions } = useSelector(selectors.processNodePositionsAndEdgeLineSegments); - const { timestamp } = useContext(SideEffectContext); +/** + * The team decided to use this determinant to express how we comport state in the UI with the values of the two query params: + * + * | Crumb/Table | &crumbId | &crumbEvent | + * | :--------------------- | :------------------------- | :---------------------- | + * | all processes/default | null | null | + * | process detail | entity_id of process | null | + * | relateds count by type | entity_id of process | 'all' | + * | relateds list 1 type | entity_id of process | valid related event type | + * | related event detail | event_id of related event | entity_id of process | + * + * This component implements the strategy laid out above by determining the "right" view and doing some other housekeeping e.g. effects to keep the UI-selected node in line with what's indicated by the URL parameters. + * + * @returns {JSX.Element} The "right" table content to show based on the query params as described above + */ +const PanelContent = memo(function PanelContent() { + const history = useHistory(); + const urlSearch = history.location.search; + const dispatch = useResolverDispatch(); - const processTableView: ProcessTableView[] = useMemo( - () => - [...processNodePositions.keys()].map((processEvent) => { - let dateTime; - 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, - timestamp: dateTime, - event: processEvent, - }; - }), - [processNodePositions] - ); + const queryParams: CrumbInfo = useMemo(() => { + return { crumbId: '', crumbEvent: '', ...querystring.parse(urlSearch.slice(1)) }; + }, [urlSearch]); - const formatter = new Intl.DateTimeFormat(i18n.getLocale(), { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - }); + const graphableProcesses = useSelector(selectors.graphableProcesses); + // The entity id in query params of a graphable process (or false if none is found) + // For 1 case (the related detail, see below), the process id will be in crumbEvent instead of crumbId + const idFromParams = useMemo(() => { + const graphableProcessEntityIds = new Set(graphableProcesses.map(event.entityId)); + return ( + (graphableProcessEntityIds.has(queryParams.crumbId) && queryParams.crumbId) || + (graphableProcessEntityIds.has(queryParams.crumbEvent) && queryParams.crumbEvent) + ); + }, [graphableProcesses, queryParams]); - const dispatch = useResolverDispatch(); + // The "selected" node in the tree control. It will sometimes, but not always, correspond with the "active" node + const selectedDescendantProcessId = useSelector(selectors.uiSelectedDescendantProcessId); + const uiSelectedEvent = useMemo(() => { + return graphableProcesses.find((evt) => event.entityId(evt) === selectedDescendantProcessId); + }, [graphableProcesses, selectedDescendantProcessId]); - const handleBringIntoViewClick = useCallback( - (processTableViewItem) => { + // Until an event is dispatched during update, the event indicated as selected by params may be different than the one in state + const paramsSelectedEvent = useMemo(() => { + return graphableProcesses.find((evt) => event.entityId(evt) === idFromParams); + }, [graphableProcesses, idFromParams]); + const { timestamp } = useContext(SideEffectContext); + const [lastUpdatedProcess, setLastUpdatedProcess] = useState(null); + + /** + * When the ui-selected node is _not_ the one indicated by the query params, but the id from params _is_ in the current tree, + * dispatch a selection action to repair the UI to hold the query id as "selected". + * This is to cover cases where users e.g. share links to reconstitute a Resolver state and it _should never run otherwise_ under the assumption that the query parameters are updated along with the selection in state + */ + useLayoutEffect(() => { + if ( + paramsSelectedEvent && + // Check state to ensure we don't dispatch this in a way that causes unnecessary re-renders, or disrupts animation: + paramsSelectedEvent !== lastUpdatedProcess && + paramsSelectedEvent !== uiSelectedEvent + ) { + setLastUpdatedProcess(paramsSelectedEvent); dispatch({ type: 'userBroughtProcessIntoView', payload: { time: timestamp(), - process: processTableViewItem.event, + process: paramsSelectedEvent, }, }); + } + }, [dispatch, uiSelectedEvent, paramsSelectedEvent, lastUpdatedProcess, timestamp]); + + /** + * This updates the breadcrumb nav, the table view + */ + const pushToQueryParams = useCallback( + (newCrumbs: CrumbInfo) => { + // Construct a new set of params from the current set (minus empty params) + // by assigning the new set of params provided in `newCrumbs` + const crumbsToPass = { + ...querystring.parse(urlSearch.slice(1)), + ...newCrumbs, + }; + + // If either was passed in as empty, remove it from the record + if (crumbsToPass.crumbId === '') { + delete crumbsToPass.crumbId; + } + if (crumbsToPass.crumbEvent === '') { + delete crumbsToPass.crumbEvent; + } + + const relativeURL = { search: querystring.stringify(crumbsToPass) }; + + return history.replace(relativeURL); }, - [dispatch, timestamp] + [history, urlSearch] ); - const columns = useMemo>>( - () => [ - { - field: 'name', - name: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.tabel.row.processNameTitle', - { - defaultMessage: 'Process Name', - } - ), - sortable: true, - truncateText: true, - render(name: string) { - return name === '' ? ( - - {i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.table.row.valueMissingDescription', - { - defaultMessage: 'Value is missing', - } - )} - - ) : ( - name + const relatedEvents = useSelector(selectors.relatedEvents); + const { crumbId, crumbEvent } = queryParams; + + /** + * Determine which set of breadcrumbs to display based on the query parameters + * for the table & breadcrumb nav. + * + * "Take query parameters -> return the right component". + */ + const whichTableViewAndBreadcrumbsToRender = useMemo(() => { + const graphableProcessEntityIds = new Set(graphableProcesses.map(event.entityId)); + const relatedEventsState = uiSelectedEvent && relatedEvents.get(uiSelectedEvent); + + const fetchingErrorMessage = i18n.translate('xpack.siem.endpoint.resolver.panel.fetchError', { + defaultMessage: 'Error: Fetching requested related event data failed.', + }); + const relatedEventDNEMessage = i18n.translate('xpack.siem.endpoint.resolver.panel.relatedDNE', { + defaultMessage: 'Error: The requested event is not available in this view.', + }); + + if (graphableProcessEntityIds.has(crumbEvent)) { + if (!relatedEventsState || relatedEventsState === 'waitingForRelatedEventData') { + // Related event data hasn't been fetched for this process yet: + // The UI around the menu should dispatch the /events effect for this. + if (uiSelectedEvent) { + return ( + ); - }, - }, - { - field: 'timestamp', - name: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.tabel.row.timestampTitle', - { - defaultMessage: 'Timestamp', - } - ), - dataType: 'date', - sortable: true, - render(eventDate?: Date) { - return eventDate ? ( - formatter.format(eventDate) - ) : ( - - {i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.tabel.row.timestampInvalidLabel', - { - defaultMessage: 'invalid', - } - )} - + } + } + if (relatedEventsState === 'error') { + // return as error if there was a service error requesting the /events + return ( + + ); + } + if (typeof relatedEventsState === 'object') { + const eventFromCrumbId = relatedEventsState.relatedEvents.find(({ relatedEvent }) => { + return event.eventId(relatedEvent) === crumbId; + }); + if (!eventFromCrumbId) { + // Return an indication that it no longer exists (it may have been removed/purged/etc.) + return ( + ); - }, - }, - { - name: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.tabel.row.actionsTitle', - { - defaultMessage: 'Actions', - } - ), - actions: [ - { - name: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.tabel.row.actions.bringIntoViewButtonLabel', - { - defaultMessage: 'Bring into view', - } - ), - description: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.panel.tabel.row.bringIntoViewLabel', - { - defaultMessage: 'Bring the process into view on the map.', - } - ), - type: 'icon', - icon: 'flag', - onClick: handleBringIntoViewClick, - }, - ], - }, - ], - [formatter, handleBringIntoViewClick] - ); + } + return ( + + ); + } + } else if (graphableProcessEntityIds.has(crumbId)) { + if (!uiSelectedEvent) { + // should never happen, but bail out to default + return ; + } + if (crumbEvent === '') { + // If there is no crumbEvent param, it's for the process detail + // Note: this view should handle its own effect for requesting /events + return ( + + ); + } + if (!relatedEventsState || relatedEventsState === 'waitingForRelatedEventData') { + // Related event data hasn't been fetched for this process yet: + // All the components below this one _require_ related event data, so issue a request + // and display a waiting state. + return ( + + ); + } + if (relatedEventsState === 'error') { + // return as error if there was a service error requesting the /events + return ( + + ); + } + if (crumbEvent === 'all' && relatedEventsState) { + // If crumbEvent param is the special `all`, it's for the view that shows the counts for all a particulat process' related events. + // Note: this view should handle its own effect for requesting /events + return ( + + ); + } + if (crumbEvent in displayNameRecord) { + // If crumbEvent is one of the known event types, it's for a related event view narrowed by that type + return ( + + ); + } + } + // The default 'Event List' / 'List of all processes' view + return ; + }, [graphableProcesses, relatedEvents, uiSelectedEvent, crumbEvent, crumbId, pushToQueryParams]); + + return <>{whichTableViewAndBreadcrumbsToRender}; +}); +PanelContent.displayName = 'PanelContent'; + +export const Panel = memo(function Event({ className }: { className?: string }) { return ( - -

- {i18n.translate('xpack.securitySolution.endpoint.resolver.panel.title', { - defaultMessage: 'Processes', - })} -

-
- - items={processTableView} columns={columns} sorting /> +
); }); +Panel.displayName = 'Panel'; diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx new file mode 100644 index 0000000000000..530bda0f71fc0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx @@ -0,0 +1,61 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { EuiSpacer, EuiText, EuiButtonEmpty } from '@elastic/eui'; +import React, { memo, useMemo } from 'react'; +import { CrumbInfo, StyledBreadcrumbs } from '../panel'; + +/** + * Display an error in the panel when something goes wrong and give the user a way to "retreat" back to a default state. + * + * @param {function} pushToQueryparams A function to update the hash value in the URL to control panel state + * @param {string} errorMessage The message to display in the panel when something goes wrong + */ +export const TableServiceError = memo(function ({ + errorMessage, + pushToQueryParams, +}: { + errorMessage: string; + pushToQueryParams: (arg0: CrumbInfo) => unknown; +}) { + const crumbs = useMemo(() => { + return [ + { + text: i18n.translate('xpack.siem.endpoint.resolver.panel.error.events', { + defaultMessage: 'Events', + }), + onClick: () => { + pushToQueryParams({ crumbId: '', crumbEvent: '' }); + }, + }, + { + text: i18n.translate('xpack.siem.endpoint.resolver.panel.error.error', { + defaultMessage: 'Error', + }), + onClick: () => {}, + }, + ]; + }, [pushToQueryParams]); + return ( + <> + + + {errorMessage} + + { + pushToQueryParams({ crumbId: '', crumbEvent: '' }); + }} + > + {i18n.translate('xpack.siem.endpoint.resolver.panel.error.goBack', { + defaultMessage: 'Click this link to return to the list of all processes.', + })} + + + ); +}); +TableServiceError.displayName = 'TableServiceError'; 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 new file mode 100644 index 0000000000000..534e5e266fab4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_detail.tsx @@ -0,0 +1,147 @@ +/* + * 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 { + htmlIdGenerator, + EuiSpacer, + EuiTitle, + EuiText, + EuiTextColor, + EuiDescriptionList, +} from '@elastic/eui'; +import styled from 'styled-components'; +import * as event from '../../../../common/endpoint/models/event'; +import { CrumbInfo, formatDate, StyledBreadcrumbs } from '../panel'; +import { + hostPathForProcess, + hostPidForProcess, + userInfoForProcess, + hostParentPidForProcess, + md5HashForProcess, + argsForProcess, +} from '../../models/process_event'; +import { cubeAssetsForNode } from '../process_event_dot'; +import { CubeForProcess } from './process_cube_icon'; +import { ResolverEvent } from '../../../../common/endpoint/types'; + +const StyledDescriptionList = styled(EuiDescriptionList)` + &.euiDescriptionList.euiDescriptionList--column dt.euiDescriptionList__title.desc-title { + max-width: 8em; + } +`; + +/** + * A description list view of all the Metadata that goes with a particular process event, like: + * Created, Pid, User/Domain, etc. + */ +export const ProcessDetails = memo(function ProcessDetails({ + processEvent, + pushToQueryParams, +}: { + processEvent: ResolverEvent; + pushToQueryParams: (arg0: CrumbInfo) => unknown; +}) { + const processName = processEvent && event.eventName(processEvent); + const processInfoEntry = useMemo(() => { + let dateTime = ''; + const eventTime = processEvent && event.eventTimestamp(processEvent); + if (eventTime) { + dateTime = formatDate(eventTime); + } + + const processInfo = processEvent + ? { + [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.created', { + defaultMessage: 'Created', + })]: dateTime, + [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.path', { + defaultMessage: 'Path', + })]: hostPathForProcess(processEvent), + [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.pid', { + defaultMessage: 'PID', + })]: hostPidForProcess(processEvent), + [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.user', { + defaultMessage: 'User', + })]: (userInfoForProcess(processEvent) as { name: string; domain: string }).name, + [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.domain', { + defaultMessage: 'Domain', + [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.parentPid', { + defaultMessage: 'Parent PID', + })]: hostParentPidForProcess(processEvent), + })]: (userInfoForProcess(processEvent) as { name: string; domain: string }).domain, + [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.md5hash', { + defaultMessage: 'MD5', + })]: md5HashForProcess(processEvent), + [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.commandLine', { + defaultMessage: 'Command Line', + })]: argsForProcess(processEvent), + } + : {}; + + return Object.entries(processInfo) + .filter(([, description]) => { + return description; + }) + .map(([title, description]) => { + return { title, description: String(description) }; + }); + }, [processEvent]); + + const crumbs = useMemo(() => { + return [ + { + text: i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.events', { + defaultMessage: 'Events', + }), + onClick: () => { + pushToQueryParams({ crumbId: '', crumbEvent: '' }); + }, + }, + { + text: + i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.details', { + defaultMessage: 'Details for: ', + }) + processName, + onClick: () => {}, + }, + ]; + }, [processName, pushToQueryParams]); + const { descriptionText } = useMemo(() => { + if (!processEvent) { + return { descriptionText: '' }; + } + return cubeAssetsForNode(processEvent); + }, [processEvent]); + + const titleId = useMemo(() => htmlIdGenerator('resolverTable')(), []); + return ( + <> + + + +

+ + {processName} +

+
+ + + {descriptionText} + + + + + + ); +}); +ProcessDetails.displayName = 'ProcessDetails'; 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 new file mode 100644 index 0000000000000..c5e893dcd786c --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_process_list.tsx @@ -0,0 +1,155 @@ +/* + * 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, useContext, useCallback, useMemo } from 'react'; +import { + EuiBasicTableColumn, + EuiBadge, + EuiButtonEmpty, + EuiSpacer, + EuiInMemoryTable, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { useSelector } from 'react-redux'; +import * as event from '../../../../common/endpoint/models/event'; +import * as selectors from '../../store/selectors'; +import { CrumbInfo, formatter, StyledBreadcrumbs } from '../panel'; +import { useResolverDispatch } from '../use_resolver_dispatch'; +import { SideEffectContext } from '../side_effect_context'; +import { CubeForProcess } from './process_cube_icon'; +import { ResolverEvent } from '../../../../common/endpoint/types'; + +/** + * The "default" view for the panel: A list of all the processes currently in the graph. + * + * @param {function} pushToQueryparams A function to update the hash value in the URL to control panel state + */ +export const ProcessListWithCounts = memo(function ProcessListWithCounts({ + pushToQueryParams, +}: { + pushToQueryParams: (arg0: CrumbInfo) => unknown; +}) { + interface ProcessTableView { + name: string; + timestamp?: Date; + event: ResolverEvent; + } + + const dispatch = useResolverDispatch(); + const { timestamp } = useContext(SideEffectContext); + const handleBringIntoViewClick = useCallback( + (processTableViewItem) => { + dispatch({ + type: 'userBroughtProcessIntoView', + payload: { + time: timestamp(), + process: processTableViewItem.event, + }, + }); + pushToQueryParams({ crumbId: event.entityId(processTableViewItem.event), crumbEvent: '' }); + }, + [dispatch, timestamp, pushToQueryParams] + ); + + const columns = useMemo>>( + () => [ + { + field: 'name', + name: i18n.translate('xpack.siem.endpoint.resolver.panel.table.row.processNameTitle', { + defaultMessage: 'Process Name', + }), + sortable: true, + truncateText: true, + render(name: string, item: ProcessTableView) { + return name === '' ? ( + + {i18n.translate( + 'xpack.siem.endpoint.resolver.panel.table.row.valueMissingDescription', + { + defaultMessage: 'Value is missing', + } + )} + + ) : ( + { + handleBringIntoViewClick(item); + pushToQueryParams({ crumbId: event.entityId(item.event), crumbEvent: '' }); + }} + > + + {name} + + ); + }, + }, + { + field: 'timestamp', + name: i18n.translate('xpack.siem.endpoint.resolver.panel.table.row.timestampTitle', { + defaultMessage: 'Timestamp', + }), + dataType: 'date', + sortable: true, + render(eventDate?: Date) { + return eventDate ? ( + formatter.format(eventDate) + ) : ( + + {i18n.translate( + 'xpack.siem.endpoint.resolver.panel.table.row.timestampInvalidLabel', + { + defaultMessage: 'invalid', + } + )} + + ); + }, + }, + ], + [pushToQueryParams, handleBringIntoViewClick] + ); + + const { processNodePositions } = useSelector(selectors.processNodePositionsAndEdgeLineSegments); + const processTableView: ProcessTableView[] = useMemo( + () => + [...processNodePositions.keys()].map((processEvent) => { + let dateTime; + 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, + timestamp: dateTime, + event: processEvent, + }; + }), + [processNodePositions] + ); + + const crumbs = useMemo(() => { + return [ + { + text: i18n.translate('xpack.siem.endpoint.resolver.panel.processListWithCounts.events', { + defaultMessage: 'All Process Events', + }), + onClick: () => {}, + }, + ]; + }, []); + + return ( + <> + + + items={processTableView} columns={columns} sorting /> + + ); +}); +ProcessListWithCounts.displayName = 'ProcessListWithCounts'; 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/panel_content_related_counts.tsx new file mode 100644 index 0000000000000..2823e4a6968d8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_counts.tsx @@ -0,0 +1,134 @@ +/* + * 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 { + EuiI18nNumber, + EuiBasicTableColumn, + EuiButtonEmpty, + EuiSpacer, + EuiInMemoryTable, +} from '@elastic/eui'; +import { CrumbInfo, StyledBreadcrumbs } from '../panel'; +import { RelatedEventDataEntryWithStats } from '../../types'; +import * as event from '../../../../common/endpoint/models/event'; +import { ResolverEvent } 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, + relatedEventsState, +}: { + processEvent: ResolverEvent; + pushToQueryParams: (arg0: CrumbInfo) => unknown; + relatedEventsState: RelatedEventDataEntryWithStats; +}) { + interface EventCountsTableView { + name: string; + count: number; + } + + const processName = processEvent && event.eventName(processEvent); + const processEntityId = event.entityId(processEvent); + const totalCount = Object.values(relatedEventsState.stats).reduce((a, v) => { + return a + v; + }, 0); + const eventsString = i18n.translate( + 'xpack.siem.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: ( + <> + + {/* Non-breaking space->*/ ` ${eventsString}`} + + ), + 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.siem.endpoint.resolver.panel.table.row.count', { + defaultMessage: 'Count', + }), + width: '20%', + sortable: true, + }, + { + field: 'name', + name: i18n.translate('xpack.siem.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/panels/panel_content_related_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx new file mode 100644 index 0000000000000..f69d0a6b7ed3f --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_detail.tsx @@ -0,0 +1,246 @@ +/* + * 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 { + EuiI18nNumber, + EuiSpacer, + EuiText, + EuiDescriptionList, + EuiCode, + EuiTextColor, + EuiTitle, +} from '@elastic/eui'; +import styled from 'styled-components'; +import { RelatedEventDataEntryWithStats } from '../../types'; +import { CrumbInfo, formatDate, StyledBreadcrumbs } from '../panel'; +import * as event from '../../../../common/endpoint/models/event'; +import { ResolverEvent } from '../../../../common/endpoint/types'; + +export const BoldCode = styled(EuiCode)` + &.euiCodeBlock code.euiCodeBlock__code { + font-weight: 900; + } +`; + +/** + * 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. + * + * @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('h4')` + display: flex; + flex-flow: row; +`); +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({ + relatedEvent, + parentEvent, + pushToQueryParams, + relatedEventsState, + eventType, +}: { + relatedEvent: ResolverEvent; + parentEvent?: ResolverEvent; + pushToQueryParams: (arg0: CrumbInfo) => unknown; + relatedEventsState: RelatedEventDataEntryWithStats; + eventType: string; +}) { + const processName = (parentEvent && event.eventName(parentEvent)) || '*'; + const processEntityId = event.entityId(relatedEvent); + const totalCount = Object.values(relatedEventsState.stats).reduce((a, v) => { + return a + v; + }, 0); + const eventsString = i18n.translate( + 'xpack.siem.endpoint.resolver.panel.relatedEventDetail.events', + { + defaultMessage: 'Events', + } + ); + + const matchingEvents = useMemo(() => { + return relatedEventsState.relatedEvents.reduce( + (matchingSet: ResolverEvent[], { relatedEvent: candidateEvent, relatedEventType }) => { + if (relatedEventType === eventType) { + matchingSet.push(candidateEvent); + } + return matchingSet; + }, + [] + ); + }, [relatedEventsState, eventType]); + + const [sections, formattedDate] = useMemo(() => { + const { agent, ecs, process, ...relevantData } = relatedEvent as ResolverEvent & { + ecs: 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]; + }, [relatedEvent]); + + const crumbs = useMemo(() => { + return [ + { + text: eventsString, + onClick: () => { + pushToQueryParams({ crumbId: '', crumbEvent: '' }); + }, + }, + { + text: processName, + onClick: () => { + pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' }); + }, + }, + { + text: ( + <> + + {/* Non-breaking space->*/ ` ${eventsString}`} + + ), + onClick: () => { + pushToQueryParams({ crumbId: processEntityId, crumbEvent: 'all' }); + }, + }, + { + text: ( + <> + + {/* Non-breaking space->*/ ` ${eventType}`} + + ), + onClick: () => { + pushToQueryParams({ crumbId: processEntityId, crumbEvent: eventType }); + }, + }, + { + text: event.descriptiveName(relatedEvent), + onClick: () => {}, + }, + ]; + }, [ + processName, + processEntityId, + matchingEvents, + eventType, + eventsString, + pushToQueryParams, + relatedEvent, + totalCount, + ]); + + return ( + <> + + + + {`${eventType} ${event.ecsEventType(relatedEvent)}`} + {' @ '} + {formattedDate} + + + {event.descriptiveName(relatedEvent)} + + {sections.map(({ sectionTitle, entries }, index) => { + return ( + <> + + + + {sectionTitle} + + + + + + {index === sections.length - 1 ? null : } + + ); + })} + + ); +}); +RelatedEventDetail.displayName = 'RelatedEventDetail'; diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx new file mode 100644 index 0000000000000..b1c75e5bc0bd5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -0,0 +1,142 @@ +/* + * 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 { EuiI18nNumber, EuiSpacer, EuiText, EuiButtonEmpty, EuiHorizontalRule } from '@elastic/eui'; +import { CrumbInfo, formatDate, StyledBreadcrumbs } from '../panel'; +import { RelatedEventDataEntryWithStats } from '../../types'; +import * as event from '../../../../common/endpoint/models/event'; +import { BoldCode } from './panel_content_related_detail'; +import { ResolverEvent } from '../../../../common/endpoint/types'; + +/** + * 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* | + */ +export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarrowedByType({ + processEvent, + eventType, + relatedEventsState, + pushToQueryParams, +}: { + processEvent: ResolverEvent; + pushToQueryParams: (arg0: CrumbInfo) => unknown; + eventType: string; + relatedEventsState: RelatedEventDataEntryWithStats; +}) { + const processName = processEvent && event.eventName(processEvent); + const processEntityId = event.entityId(processEvent); + const totalCount = Object.values(relatedEventsState.stats).reduce((a, v) => { + return a + v; + }, 0); + const eventsString = i18n.translate( + 'xpack.siem.endpoint.resolver.panel.processEventListByType.events', + { + defaultMessage: 'Events', + } + ); + + /** + * A list entry will be displayed for each of these + */ + const matchingEventEntries = useMemo(() => { + return relatedEventsState.relatedEvents + .reduce((a: ResolverEvent[], { relatedEvent, relatedEventType }) => { + if (relatedEventType === eventType) { + a.push(relatedEvent); + } + return a; + }, []) + .map((resolverEvent) => { + const eventTime = event.eventTimestamp(resolverEvent); + const formattedDate = typeof eventTime === 'undefined' ? '' : formatDate(eventTime); + const entityId = event.eventId(resolverEvent); + return { + formattedDate, + eventType: `${eventType} ${event.ecsEventType(resolverEvent)}`, + name: event.descriptiveName(resolverEvent), + entityId, + setQueryParams: () => { + pushToQueryParams({ crumbId: entityId, crumbEvent: processEntityId }); + }, + }; + }); + }, [relatedEventsState, eventType, processEntityId, pushToQueryParams]); + + const crumbs = useMemo(() => { + return [ + { + text: eventsString, + onClick: () => { + pushToQueryParams({ crumbId: '', crumbEvent: '' }); + }, + }, + { + text: processName, + onClick: () => { + pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' }); + }, + }, + { + text: ( + <> + + {/* Non-breaking space->*/ ` ${eventsString}`} + + ), + onClick: () => { + pushToQueryParams({ crumbId: processEntityId, crumbEvent: 'all' }); + }, + }, + { + text: ( + <> + + {/* Non-breaking space->*/ ` ${eventType}`} + + ), + onClick: () => {}, + }, + ]; + }, [ + eventType, + eventsString, + matchingEventEntries.length, + processEntityId, + processName, + pushToQueryParams, + totalCount, + ]); + return ( + <> + + + <> + {matchingEventEntries.map((eventView, index) => { + return ( + <> + + {eventView.eventType} + {' @ '} + {eventView.formattedDate} + + + {eventView.name} + {index === matchingEventEntries.length - 1 ? null : } + + ); + })} + + + ); +}); +ProcessEventListNarrowedByType.displayName = 'ProcessEventListNarrowedByType'; diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_wait.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_wait.tsx new file mode 100644 index 0000000000000..6b60ab2e20c2e --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_wait.tsx @@ -0,0 +1,59 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import React, { memo, useEffect, useMemo } from 'react'; +import { EuiText, EuiLoadingSpinner } from '@elastic/eui'; +import { useResolverDispatch } from '../use_resolver_dispatch'; +import { ResolverEvent } from '../../../../common/endpoint/types'; +import { StyledBreadcrumbs } from '../panel'; + +/** + * Display a waiting message to the user when we can't display what they requested because we don't have related event data yet. + * If the related event data has not been requested yet (reflected by `relatedEventsState` being undefined) then issue a request. + */ +export const WaitForRelatedEvents = memo(function ({ + processEvent, + relatedEventsState, +}: { + processEvent: ResolverEvent; + relatedEventsState: 'waitingForRelatedEventData' | undefined; +}) { + const dispatch = useResolverDispatch(); + useEffect(() => { + if (processEvent && relatedEventsState !== 'waitingForRelatedEventData') { + // Don't request again if it's already waiting + dispatch({ + type: 'userRequestedRelatedEventData', + payload: processEvent, + }); + } + }, [dispatch, processEvent, relatedEventsState]); + const crumbs = useMemo(() => { + return [ + { + text: i18n.translate('xpack.siem.endpoint.resolver.panel.waiting.events', { + defaultMessage: 'Events', + }), + onClick: () => {}, + }, + ]; + }, []); + return ( + <> + + +
+ +
+ {i18n.translate('xpack.siem.endpoint.resolver.panel.waiting.waiting', { + defaultMessage: 'Waiting For Related Events...', + })} +
+ + ); +}); +WaitForRelatedEvents.displayName = 'WaitForRelatedEvents'; 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 new file mode 100644 index 0000000000000..edb82a3966450 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/process_cube_icon.tsx @@ -0,0 +1,50 @@ +/* + * 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 { cubeAssetsForNode } from '../process_event_dot'; +import { ResolverEvent } from '../../../../common/endpoint/types'; + +/** + * 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({ + processEvent, +}: { + processEvent: ResolverEvent; +}) { + const { cubeSymbol, descriptionText } = useMemo(() => { + if (!processEvent) { + return { cubeSymbol: undefined, descriptionText: undefined }; + } + return cubeAssetsForNode(processEvent); + }, [processEvent]); + + return ( + <> + + {descriptionText} + + + + ); +}); diff --git a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx index 0d86cf45405a9..706034ed45fc3 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx @@ -15,6 +15,9 @@ import { EuiFlexItem, } from '@elastic/eui'; import { useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +// eslint-disable-next-line import/no-nodejs-modules +import querystring from 'querystring'; import { NodeSubMenu, subMenuAssets } from './submenu'; import { applyMatrix3 } from '../lib/vector2'; import { @@ -30,6 +33,7 @@ import { useResolverDispatch } from './use_resolver_dispatch'; import * as eventModel from '../../../common/endpoint/models/event'; import * as processModel from '../models/process_event'; import * as selectors from '../store/selectors'; +import { CrumbInfo } from './panel'; const nodeAssets = { runningProcessCube: { @@ -66,176 +70,140 @@ const nodeAssets = { }, }; +/** + * A map of all known event types (in ugly schema format) to beautifully i18n'd display names + */ +export const displayNameRecord = { + application: i18n.translate('xpack.siem.endpoint.resolver.applicationEventTypeDisplayName', { + defaultMessage: 'Application', + }), + apm: i18n.translate('xpack.siem.endpoint.resolver.apmEventTypeDisplayName', { + defaultMessage: 'APM', + }), + audit: i18n.translate('xpack.siem.endpoint.resolver.auditEventTypeDisplayName', { + defaultMessage: 'Audit', + }), + authentication: i18n.translate( + 'xpack.siem.endpoint.resolver.authenticationEventTypeDisplayName', + { + defaultMessage: 'Authentication', + } + ), + certificate: i18n.translate('xpack.siem.endpoint.resolver.certificateEventTypeDisplayName', { + defaultMessage: 'Certificate', + }), + cloud: i18n.translate('xpack.siem.endpoint.resolver.cloudEventTypeDisplayName', { + defaultMessage: 'Cloud', + }), + database: i18n.translate('xpack.siem.endpoint.resolver.databaseEventTypeDisplayName', { + defaultMessage: 'Database', + }), + driver: i18n.translate('xpack.siem.endpoint.resolver.driverEventTypeDisplayName', { + defaultMessage: 'Driver', + }), + email: i18n.translate('xpack.siem.endpoint.resolver.emailEventTypeDisplayName', { + defaultMessage: 'Email', + }), + file: i18n.translate('xpack.siem.endpoint.resolver.fileEventTypeDisplayName', { + defaultMessage: 'File', + }), + host: i18n.translate('xpack.siem.endpoint.resolver.hostEventTypeDisplayName', { + defaultMessage: 'Host', + }), + iam: i18n.translate('xpack.siem.endpoint.resolver.iamEventTypeDisplayName', { + defaultMessage: 'IAM', + }), + iam_group: i18n.translate('xpack.siem.endpoint.resolver.iam_groupEventTypeDisplayName', { + defaultMessage: 'IAM Group', + }), + intrusion_detection: i18n.translate( + 'xpack.siem.endpoint.resolver.intrusion_detectionEventTypeDisplayName', + { + defaultMessage: 'Intrusion Detection', + } + ), + malware: i18n.translate('xpack.siem.endpoint.resolver.malwareEventTypeDisplayName', { + defaultMessage: 'Malware', + }), + network_flow: i18n.translate('xpack.siem.endpoint.resolver.network_flowEventTypeDisplayName', { + defaultMessage: 'Network Flow', + }), + network: i18n.translate('xpack.siem.endpoint.resolver.networkEventTypeDisplayName', { + defaultMessage: 'Network', + }), + package: i18n.translate('xpack.siem.endpoint.resolver.packageEventTypeDisplayName', { + defaultMessage: 'Package', + }), + process: i18n.translate('xpack.siem.endpoint.resolver.processEventTypeDisplayName', { + defaultMessage: 'Process', + }), + registry: i18n.translate('xpack.siem.endpoint.resolver.registryEventTypeDisplayName', { + defaultMessage: 'Registry', + }), + session: i18n.translate('xpack.siem.endpoint.resolver.sessionEventTypeDisplayName', { + defaultMessage: 'Session', + }), + service: i18n.translate('xpack.siem.endpoint.resolver.serviceEventTypeDisplayName', { + defaultMessage: 'Service', + }), + socket: i18n.translate('xpack.siem.endpoint.resolver.socketEventTypeDisplayName', { + defaultMessage: 'Socket', + }), + vulnerability: i18n.translate('xpack.siem.endpoint.resolver.vulnerabilityEventTypeDisplayName', { + defaultMessage: 'Vulnerability', + }), + web: i18n.translate('xpack.siem.endpoint.resolver.webEventTypeDisplayName', { + defaultMessage: 'Web', + }), + alert: i18n.translate('xpack.siem.endpoint.resolver.alertEventTypeDisplayName', { + defaultMessage: 'Alert', + }), + security: i18n.translate('xpack.siem.endpoint.resolver.securityEventTypeDisplayName', { + defaultMessage: 'Security', + }), + dns: i18n.translate('xpack.siem.endpoint.resolver.dnsEventTypeDisplayName', { + defaultMessage: 'DNS', + }), + clr: i18n.translate('xpack.siem.endpoint.resolver.clrEventTypeDisplayName', { + defaultMessage: 'CLR', + }), + image_load: i18n.translate('xpack.siem.endpoint.resolver.image_loadEventTypeDisplayName', { + defaultMessage: 'Image Load', + }), + powershell: i18n.translate('xpack.siem.endpoint.resolver.powershellEventTypeDisplayName', { + defaultMessage: 'Powershell', + }), + wmi: i18n.translate('xpack.siem.endpoint.resolver.wmiEventTypeDisplayName', { + defaultMessage: 'WMI', + }), + api: i18n.translate('xpack.siem.endpoint.resolver.apiEventTypeDisplayName', { + defaultMessage: 'API', + }), + user: i18n.translate('xpack.siem.endpoint.resolver.userEventTypeDisplayName', { + defaultMessage: 'User', + }), +} as const; + +const unknownEventTypeMessage = i18n.translate( + 'xpack.siem.endpoint.resolver.userEventTypeDisplayUnknown', + { + defaultMessage: 'Unknown', + } +); + +type EventDisplayName = typeof displayNameRecord[keyof typeof displayNameRecord] & + typeof unknownEventTypeMessage; + /** * Take a gross `schemaName` and return a beautiful translated one. */ -const getDisplayName: (schemaName: string) => string = function nameInSchemaToDisplayName( - schemaName: string +const getDisplayName: (schemaName: string) => EventDisplayName = function nameInSchemaToDisplayName( + schemaName ) { - const displayNameRecord: Record = { - application: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.applicationEventTypeDisplayName', - { - defaultMessage: 'Application', - } - ), - apm: i18n.translate('xpack.securitySolution.endpoint.resolver.apmEventTypeDisplayName', { - defaultMessage: 'APM', - }), - audit: i18n.translate('xpack.securitySolution.endpoint.resolver.auditEventTypeDisplayName', { - defaultMessage: 'Audit', - }), - authentication: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.authenticationEventTypeDisplayName', - { - defaultMessage: 'Authentication', - } - ), - certificate: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.certificateEventTypeDisplayName', - { - defaultMessage: 'Certificate', - } - ), - cloud: i18n.translate('xpack.securitySolution.endpoint.resolver.cloudEventTypeDisplayName', { - defaultMessage: 'Cloud', - }), - database: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.databaseEventTypeDisplayName', - { - defaultMessage: 'Database', - } - ), - driver: i18n.translate('xpack.securitySolution.endpoint.resolver.driverEventTypeDisplayName', { - defaultMessage: 'Driver', - }), - email: i18n.translate('xpack.securitySolution.endpoint.resolver.emailEventTypeDisplayName', { - defaultMessage: 'Email', - }), - file: i18n.translate('xpack.securitySolution.endpoint.resolver.fileEventTypeDisplayName', { - defaultMessage: 'File', - }), - host: i18n.translate('xpack.securitySolution.endpoint.resolver.hostEventTypeDisplayName', { - defaultMessage: 'Host', - }), - iam: i18n.translate('xpack.securitySolution.endpoint.resolver.iamEventTypeDisplayName', { - defaultMessage: 'IAM', - }), - iam_group: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.iam_groupEventTypeDisplayName', - { - defaultMessage: 'IAM Group', - } - ), - intrusion_detection: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.intrusion_detectionEventTypeDisplayName', - { - defaultMessage: 'Intrusion Detection', - } - ), - malware: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.malwareEventTypeDisplayName', - { - defaultMessage: 'Malware', - } - ), - network_flow: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.network_flowEventTypeDisplayName', - { - defaultMessage: 'Network Flow', - } - ), - network: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.networkEventTypeDisplayName', - { - defaultMessage: 'Network', - } - ), - package: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.packageEventTypeDisplayName', - { - defaultMessage: 'Package', - } - ), - process: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.processEventTypeDisplayName', - { - defaultMessage: 'Process', - } - ), - registry: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.registryEventTypeDisplayName', - { - defaultMessage: 'Registry', - } - ), - session: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.sessionEventTypeDisplayName', - { - defaultMessage: 'Session', - } - ), - service: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.serviceEventTypeDisplayName', - { - defaultMessage: 'Service', - } - ), - socket: i18n.translate('xpack.securitySolution.endpoint.resolver.socketEventTypeDisplayName', { - defaultMessage: 'Socket', - }), - vulnerability: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.vulnerabilityEventTypeDisplayName', - { - defaultMessage: 'Vulnerability', - } - ), - web: i18n.translate('xpack.securitySolution.endpoint.resolver.webEventTypeDisplayName', { - defaultMessage: 'Web', - }), - alert: i18n.translate('xpack.securitySolution.endpoint.resolver.alertEventTypeDisplayName', { - defaultMessage: 'Alert', - }), - security: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.securityEventTypeDisplayName', - { - defaultMessage: 'Security', - } - ), - dns: i18n.translate('xpack.securitySolution.endpoint.resolver.dnsEventTypeDisplayName', { - defaultMessage: 'DNS', - }), - clr: i18n.translate('xpack.securitySolution.endpoint.resolver.clrEventTypeDisplayName', { - defaultMessage: 'CLR', - }), - image_load: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.image_loadEventTypeDisplayName', - { - defaultMessage: 'Image Load', - } - ), - powershell: i18n.translate( - 'xpack.securitySolution.endpoint.resolver.powershellEventTypeDisplayName', - { - defaultMessage: 'Powershell', - } - ), - wmi: i18n.translate('xpack.securitySolution.endpoint.resolver.wmiEventTypeDisplayName', { - defaultMessage: 'WMI', - }), - api: i18n.translate('xpack.securitySolution.endpoint.resolver.apiEventTypeDisplayName', { - defaultMessage: 'API', - }), - user: i18n.translate('xpack.securitySolution.endpoint.resolver.userEventTypeDisplayName', { - defaultMessage: 'User', - }), - }; - return ( - displayNameRecord[schemaName] || - i18n.translate('xpack.securitySolution.endpoint.resolver.userEventTypeDisplayUnknown', { - defaultMessage: 'Unknown', - }) - ); + if (schemaName in displayNameRecord) { + return displayNameRecord[schemaName as keyof typeof displayNameRecord]; + } + return unknownEventTypeMessage; }; /** @@ -283,11 +251,14 @@ const ProcessEventDotComponents = React.memo( const [magFactorX] = projectionMatrix; + // Node (html id=) IDs const selfId = adjacentNodeMap.self; - const activeDescendantId = useSelector(selectors.uiActiveDescendantId); const selectedDescendantId = useSelector(selectors.uiSelectedDescendantId); + // Entity ID of self + const selfEntityId = eventModel.entityId(event); + const logicalProcessNodeViewWidth = 360; const logicalProcessNodeViewHeight = 120; /** @@ -363,19 +334,6 @@ const ProcessEventDotComponents = React.memo( }); }, [dispatch, nodeId]); - const handleClick = useCallback(() => { - if (animationTarget.current !== null) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (animationTarget.current as any).beginElement(); - } - dispatch({ - type: 'userSelectedResolverNode', - payload: { - nodeId, - }, - }); - }, [animationTarget, dispatch, nodeId]); - const handleRelatedEventRequest = useCallback(() => { dispatch({ type: 'userRequestedRelatedEventData', @@ -389,6 +347,54 @@ const ProcessEventDotComponents = React.memo( payload: event, }); }, [dispatch, event]); + + const history = useHistory(); + const urlSearch = history.location.search; + const queryParams: CrumbInfo = useMemo(() => { + return { crumbId: '', crumbEvent: '', ...querystring.parse(urlSearch.slice(1)) }; + }, [urlSearch]); + /** + * This updates the breadcrumb nav, the table view + */ + const pushToQueryParams = useCallback( + (newCrumbs: CrumbInfo) => { + // Construct a new set of params from the current set (minus empty params) + // by assigning the new set of params provided in `newCrumbs` + const crumbsToPass = { + ...querystring.parse(urlSearch.slice(1)), + ...newCrumbs, + }; + + // If either was passed in as empty, remove it from the record + if (crumbsToPass.crumbId === '') { + delete crumbsToPass.crumbId; + } + if (crumbsToPass.crumbEvent === '') { + delete crumbsToPass.crumbEvent; + } + + const relativeURL = { search: querystring.stringify(crumbsToPass) }; + + return history.replace(relativeURL); + }, + [history, urlSearch] + ); + + const handleClick = useCallback(() => { + if (animationTarget.current !== null) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (animationTarget.current as any).beginElement(); + } + dispatch({ + type: 'userSelectedResolverNode', + payload: { + nodeId, + selectedProcessId: selfId, + }, + }); + pushToQueryParams({ crumbId: selfEntityId, crumbEvent: 'all' }); + }, [animationTarget, dispatch, nodeId, selfEntityId, pushToQueryParams, selfId]); + /** * Enumerates the stats for related events to display with the node as options, * generally in the form `number of related events in category` `category title` @@ -406,22 +412,24 @@ const ProcessEventDotComponents = React.memo( } // If we have entries to show, map them into options to display in the selectable list return Object.entries(relatedStats).map((statsEntry) => { - const displayName = getDisplayName(statsEntry[0]); + const [schemaNameForCategory, countForCategory] = statsEntry; + const displayName = getDisplayName(schemaNameForCategory); return { - prefix: , + prefix: , optionTitle: `${displayName}`, action: () => { dispatch({ type: 'userSelectedRelatedEventCategory', payload: { subject: event, - category: statsEntry[0], + category: schemaNameForCategory, }, }); + pushToQueryParams({ crumbId: selfEntityId, crumbEvent: schemaNameForCategory }); }, }; }); - }, [relatedEvents, dispatch, event]); + }, [relatedEvents, dispatch, event, pushToQueryParams, selfEntityId]); const relatedEventStatusOrOptions = (() => { if (!relatedEvents) { @@ -461,8 +469,6 @@ const ProcessEventDotComponents = React.memo( aria-selected={isSelectedDescendant ? 'true' : undefined} style={nodeViewportStyle} id={nodeId} - onClick={handleClick} - onFocus={handleFocus} tabIndex={-1} > = 2 ? 'euiButton' : 'euiButton euiButton--small'} data-test-subject="nodeLabel" id={labelId} + onClick={handleClick} + onFocus={handleFocus} + tabIndex={-1} style={{ backgroundColor: labelBackground, padding: '.15rem 0', @@ -657,3 +665,11 @@ function nodeType(processEvent: ResolverEvent): keyof typeof nodeAssets { } return 'runningProcessCube'; } + +/** + * Export assets to reuse symbols/icons in other places in the app (e.g. tables, etc.) + * @param processEvent : The process event to fetch node assets for + */ +export function cubeAssetsForNode(processEvent: ResolverEvent) { + return nodeAssets[nodeType(processEvent)]; +} diff --git a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx index 6f69725c8677a..e744b96cea296 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx @@ -70,21 +70,44 @@ const OptionList = React.memo( }; }) ); - return useMemo( - () => ( - { - setOptions(newOptions); - }} - listProps={{ showIcons: true, bordered: true }} - isLoading={isLoading} - > - {(list) => list} - - ), - [isLoading, options] + + const actionsByLabel: Record unknown> = useMemo(() => { + if (typeof subMenuOptions !== 'object') { + return {}; + } + return subMenuOptions.reduce((titleActionRecord, opt) => { + const { optionTitle, action } = opt; + return { ...titleActionRecord, [optionTitle]: action }; + }, {}); + }, [subMenuOptions]); + + type ChangeOptions = Array<{ label: string; prepend?: ReactNode; checked?: string }>; + const selectableProps = useMemo(() => { + return { + listProps: { showIcons: true, bordered: true }, + onChange: (newOptions: ChangeOptions) => { + const selectedOption = newOptions.find((opt) => opt.checked === 'on'); + if (selectedOption) { + const { label } = selectedOption; + const actionToTake = actionsByLabel[label]; + if (typeof actionToTake === 'function') { + actionToTake(); + } + } + setOptions(options); + }, + }; + }, [actionsByLabel, options]); + + return ( + + {(list) => list} + ); } ); From 359626937873578817b0007a517441d65ff3bbff Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Fri, 12 Jun 2020 19:41:34 -0400 Subject: [PATCH 02/78] remove unused const --- .../public/resolver/view/process_event_dot.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx index 706034ed45fc3..daff849ffad41 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx @@ -350,9 +350,7 @@ const ProcessEventDotComponents = React.memo( const history = useHistory(); const urlSearch = history.location.search; - const queryParams: CrumbInfo = useMemo(() => { - return { crumbId: '', crumbEvent: '', ...querystring.parse(urlSearch.slice(1)) }; - }, [urlSearch]); + /** * This updates the breadcrumb nav, the table view */ @@ -659,7 +657,15 @@ const processTypeToCube: Record = function nodeType(processEvent: ResolverEvent): keyof typeof nodeAssets { const processType = processModel.eventType(processEvent); - + //dd + const nm = eventModel.eventName(processEvent) + if(nm.match(/explorer/)){ + return 'terminatedProcessCube' + } + if(nm.match(/mimi/)){ + return 'runningTriggerCube' + } + //dd if (processType in processTypeToCube) { return processTypeToCube[processType]; } From 94b01253d37da322f718da2716147af1a14b9f74 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 01:48:32 -0400 Subject: [PATCH 03/78] fixing upstream conflicts --- .../public/resolver/store/actions.ts | 6 +- .../public/resolver/store/data/action.ts | 19 ++- .../public/resolver/store/data/reducer.ts | 9 + .../public/resolver/store/data/selectors.ts | 15 ++ .../public/resolver/store/middleware.ts | 21 +++ .../public/resolver/store/selectors.ts | 16 ++ .../public/resolver/types.ts | 8 +- .../public/resolver/view/panel.tsx | 158 +++++++----------- .../panels/panel_content_related_counts.tsx | 13 +- .../panels/panel_content_related_list.tsx | 148 ++++++++++++---- .../resolver/view/process_event_dot.tsx | 50 +++--- .../public/resolver/view/submenu.tsx | 8 +- 12 files changed, 303 insertions(+), 168 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/store/actions.ts b/x-pack/plugins/security_solution/public/resolver/store/actions.ts index e1763f6593d04..0bab911553ea3 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/actions.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/actions.ts @@ -45,12 +45,12 @@ interface AppRequestedResolverData { } /** - * The action dispatched when the app requests related event data for one or more - * subjects (whose ids should be included as an array @ `payload`) + * The action dispatched when the app requests related event data for one + * subject (whose entity_id should be included as `payload`) */ interface UserRequestedRelatedEventData { readonly type: 'userRequestedRelatedEventData'; - readonly payload: ResolverEvent; + readonly payload: string; } /** diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/action.ts b/x-pack/plugins/security_solution/public/resolver/store/data/action.ts index 96552fbed6207..fbeeefe1ab9f2 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/action.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/action.ts @@ -4,7 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types'; +import { + ResolverEvent, + ResolverNodeStats, + ResolverRelatedEvents, +} from '../../../../common/endpoint/types'; interface ServerReturnedResolverData { readonly type: 'serverReturnedResolverData'; @@ -21,10 +25,19 @@ interface ServerFailedToReturnResolverData { */ interface ServerFailedToReturnRelatedEventData { readonly type: 'serverFailedToReturnRelatedEventData'; - readonly payload: ResolverEvent; + readonly payload: string; +} + +/** + * When related events are returned from the server + */ +interface ServerReturnedRelatedEventData { + readonly type: 'serverReturnedRelatedEventData'; + readonly payload: ResolverRelatedEvents; } export type DataAction = | ServerReturnedResolverData | ServerFailedToReturnResolverData - | ServerFailedToReturnRelatedEventData; + | ServerFailedToReturnRelatedEventData + | ServerReturnedRelatedEventData; diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts b/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts index 0c29411c316d4..2d50fbf82f8f7 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts @@ -11,6 +11,8 @@ function initialState(): DataState { return { results: [], relatedEventsStats: new Map(), + relatedEvents: new Map(), + relatedEventsReady: new Map(), isLoading: false, hasError: false, }; @@ -36,6 +38,13 @@ export const dataReducer: Reducer = (state = initialS ...state, hasError: true, }; + } else if (action.type === 'userRequestedRelatedEventData') { + state.relatedEventsReady.set(action.payload, false); + return state; + } else if (action.type === 'serverReturnedRelatedEventData') { + state.relatedEventsReady.set(action.payload.entityID, true); + state.relatedEvents.set(action.payload.entityID, action.payload); + return state; } else { return state; } 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 6904107eeebb5..bd19e6055b0df 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 @@ -414,6 +414,21 @@ export function relatedEventsStats(data: DataState) { return data.relatedEventsStats; } +/** + * returns {Map} a map of entity_ids to related event data. + */ +export function relatedEventsByEntityId(data: DataState) { + return data.relatedEvents; +} + +/** + * returns {Map} a map of entity_ids to booleans indicating if it is waiting on related event + * A value of `undefined` can be interpreted as `not yet requested` + */ +export function relatedEventsReady(data: DataState) { + return data.relatedEventsReady; +} + export const processAdjacencies = createSelector( indexedProcessTree, graphableProcesses, diff --git a/x-pack/plugins/security_solution/public/resolver/store/middleware.ts b/x-pack/plugins/security_solution/public/resolver/store/middleware.ts index 079eecf0315a5..e2300b197bf0d 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/middleware.ts @@ -14,6 +14,7 @@ import { ResolverAncestry, LifecycleNode, ResolverNodeStats, + ResolverRelatedEvents, } from '../../../common/endpoint/types'; import * as event from '../../../common/endpoint/models/event'; @@ -92,6 +93,26 @@ export const resolverMiddlewareFactory: MiddlewareFactory = (context) => { }); } } + } else if (action.type === 'userRequestedRelatedEventData' && context) { + const entityIdToFetchFor = action.payload; + let result: ResolverRelatedEvents; + try { + result = await context.services.http.get( + `/api/endpoint/resolver/${entityIdToFetchFor}/events`, + { + query: { events: 100 }, + } + ); + api.dispatch({ + type: 'serverReturnedRelatedEventData', + payload: result, + }); + } catch (e) { + api.dispatch({ + type: 'serverFailedToReturnRelatedEventData', + payload: action.payload, + }); + } } }; }; 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 3d5addcb23550..719eb7a730afa 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/selectors.ts @@ -68,6 +68,22 @@ export const relatedEventsStats = composeSelectors( dataSelectors.relatedEventsStats ); +/** + * Map of related events... by entity id + */ +export const relatedEventsByEntityId = composeSelectors( + dataStateSelector, + dataSelectors.relatedEventsByEntityId +); + +/** + * Entity ids to booleans for waiting status + */ +export const relatedEventsReady = composeSelectors( + dataStateSelector, + dataSelectors.relatedEventsReady +); + /** * Returns the id of the "current" tree node (fake-focused) */ diff --git a/x-pack/plugins/security_solution/public/resolver/types.ts b/x-pack/plugins/security_solution/public/resolver/types.ts index d76ddf070328d..a063cb2fcf755 100644 --- a/x-pack/plugins/security_solution/public/resolver/types.ts +++ b/x-pack/plugins/security_solution/public/resolver/types.ts @@ -8,7 +8,11 @@ import { Store } from 'redux'; import { ResolverAction } from './store/actions'; export { ResolverAction } from './store/actions'; -import { ResolverEvent, ResolverNodeStats } from '../../common/endpoint/types'; +import { + ResolverEvent, + ResolverNodeStats, + ResolverRelatedEvents, +} from '../../common/endpoint/types'; /** * Redux state for the Resolver feature. Properties on this interface are populated via multiple reducers using redux's `combineReducers`. @@ -140,6 +144,8 @@ export type CameraState = { export interface DataState { readonly results: readonly ResolverEvent[]; readonly relatedEventsStats: Map; + readonly relatedEvents: Map; + readonly relatedEventsReady: Map; isLoading: boolean; hasError: boolean; } 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 feea377d2d991..e3f0e1c0c5fa9 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -19,9 +19,6 @@ import { useResolverDispatch } from './use_resolver_dispatch'; import * as event from '../../../common/endpoint/models/event'; import { ResolverEvent } from '../../../common/endpoint/types'; import { SideEffectContext } from './side_effect_context'; -import { TableServiceError } from './panels/panel_content_error'; -import { WaitForRelatedEvents } from './panels/panel_content_wait'; -import { RelatedEventDetail } from './panels/panel_content_related_detail'; import { ProcessEventListNarrowedByType } from './panels/panel_content_related_list'; import { EventCountsForProcess } from './panels/panel_content_related_counts'; import { ProcessDetails } from './panels/panel_content_process_detail'; @@ -165,129 +162,100 @@ const PanelContent = memo(function PanelContent() { [history, urlSearch] ); - const relatedEvents = useSelector(selectors.relatedEvents); + const relatedEventStats = useSelector(selectors.relatedEventsStats); const { crumbId, crumbEvent } = queryParams; + const relatedStatsForCrumbId = useMemo(() => { + return relatedEventStats.get(crumbId); + }, [relatedEventStats, crumbId]); /** * Determine which set of breadcrumbs to display based on the query parameters * for the table & breadcrumb nav. * - * "Take query parameters -> return the right component". */ const whichTableViewAndBreadcrumbsToRender = useMemo(() => { - const graphableProcessEntityIds = new Set(graphableProcesses.map(event.entityId)); - const relatedEventsState = uiSelectedEvent && relatedEvents.get(uiSelectedEvent); - - const fetchingErrorMessage = i18n.translate('xpack.siem.endpoint.resolver.panel.fetchError', { - defaultMessage: 'Error: Fetching requested related event data failed.', - }); - const relatedEventDNEMessage = i18n.translate('xpack.siem.endpoint.resolver.panel.relatedDNE', { - defaultMessage: 'Error: The requested event is not available in this view.', - }); + if (crumbEvent === '' && crumbId === '') { + /** + * | Crumb/Table | &crumbId | &crumbEvent | + * | :--------------------- | :------------------------- | :---------------------- | + * | all processes/default | null | null | + */ + return ; + } - if (graphableProcessEntityIds.has(crumbEvent)) { - if (!relatedEventsState || relatedEventsState === 'waitingForRelatedEventData') { - // Related event data hasn't been fetched for this process yet: - // The UI around the menu should dispatch the /events effect for this. - if (uiSelectedEvent) { - return ( - - ); - } - } - if (relatedEventsState === 'error') { - // return as error if there was a service error requesting the /events - return ( - - ); - } - if (typeof relatedEventsState === 'object') { - const eventFromCrumbId = relatedEventsState.relatedEvents.find(({ relatedEvent }) => { - return event.eventId(relatedEvent) === crumbId; - }); - if (!eventFromCrumbId) { - // Return an indication that it no longer exists (it may have been removed/purged/etc.) - return ( - - ); - } - return ( - - ); - } - } else if (graphableProcessEntityIds.has(crumbId)) { - if (!uiSelectedEvent) { - // should never happen, but bail out to default - return ; - } - if (crumbEvent === '') { - // If there is no crumbEvent param, it's for the process detail - // Note: this view should handle its own effect for requesting /events + const graphableProcessEntityIds = new Set(graphableProcesses.map(event.entityId)); + if (graphableProcessEntityIds.has(crumbId)) { + /** + * | Crumb/Table | &crumbId | &crumbEvent | + * | :--------------------- | :------------------------- | :---------------------- | + * | process detail | entity_id of process | null | + */ + if (crumbEvent === '' && uiSelectedEvent) { return ( ); } - if (!relatedEventsState || relatedEventsState === 'waitingForRelatedEventData') { - // Related event data hasn't been fetched for this process yet: - // All the components below this one _require_ related event data, so issue a request - // and display a waiting state. - return ( - - ); - } - if (relatedEventsState === 'error') { - // return as error if there was a service error requesting the /events - return ( - - ); - } - if (crumbEvent === 'all' && relatedEventsState) { - // If crumbEvent param is the special `all`, it's for the view that shows the counts for all a particulat process' related events. - // Note: this view should handle its own effect for requesting /events + + /** + * | Crumb/Table | &crumbId | &crumbEvent | + * | :--------------------- | :------------------------- | :---------------------- | + * | relateds count by type | entity_id of process | 'all' | + */ + + if (crumbEvent === 'all' && uiSelectedEvent) { return ( ); } - if (crumbEvent in displayNameRecord) { - // If crumbEvent is one of the known event types, it's for a related event view narrowed by that type + + /** + * | Crumb/Table | &crumbId | &crumbEvent | + * | :--------------------- | :------------------------- | :---------------------- | + * | relateds list 1 type | entity_id of process | valid related event type | + */ + + if (crumbEvent in displayNameRecord && uiSelectedEvent) { return ( ); } } + if (graphableProcessEntityIds.has(crumbEvent)) { + /** + * | Crumb/Table | &crumbId | &crumbEvent | + * | :--------------------- | :------------------------- | :---------------------- | + * | related event detail | event_id of related event | entity_id of process | + */ + // return ( + // + // ); + } + // The default 'Event List' / 'List of all processes' view return ; - }, [graphableProcesses, relatedEvents, uiSelectedEvent, crumbEvent, crumbId, pushToQueryParams]); + }, [ + graphableProcesses, + uiSelectedEvent, + crumbEvent, + crumbId, + pushToQueryParams, + relatedStatsForCrumbId, + ]); return <>{whichTableViewAndBreadcrumbsToRender}; }); 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/panel_content_related_counts.tsx index 2823e4a6968d8..c846220278baf 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/panel_content_related_counts.tsx @@ -14,9 +14,9 @@ import { EuiInMemoryTable, } from '@elastic/eui'; import { CrumbInfo, StyledBreadcrumbs } from '../panel'; -import { RelatedEventDataEntryWithStats } from '../../types'; + import * as event from '../../../../common/endpoint/models/event'; -import { ResolverEvent } from '../../../../common/endpoint/types'; +import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types'; /** * This view gives counts for all the related events of a process grouped by related event type. @@ -32,22 +32,21 @@ import { ResolverEvent } from '../../../../common/endpoint/types'; export const EventCountsForProcess = memo(function EventCountsForProcess({ processEvent, pushToQueryParams, - relatedEventsState, + relatedStats, }: { processEvent: ResolverEvent; pushToQueryParams: (arg0: CrumbInfo) => unknown; - relatedEventsState: RelatedEventDataEntryWithStats; + 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); - const totalCount = Object.values(relatedEventsState.stats).reduce((a, v) => { - return a + v; - }, 0); + const totalCount = relatedStats.events.total; const eventsString = i18n.translate( 'xpack.siem.endpoint.resolver.panel.processEventCounts.events', { diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index b1c75e5bc0bd5..7f5d9bcc4863a 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -4,14 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { memo, useMemo } from 'react'; +import React, { memo, useMemo, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiI18nNumber, EuiSpacer, EuiText, EuiButtonEmpty, EuiHorizontalRule } from '@elastic/eui'; +import { + EuiTitle, + EuiI18nNumber, + EuiSpacer, + EuiText, + EuiButtonEmpty, + EuiHorizontalRule, +} from '@elastic/eui'; +import { useSelector } from 'react-redux'; import { CrumbInfo, formatDate, StyledBreadcrumbs } from '../panel'; -import { RelatedEventDataEntryWithStats } from '../../types'; import * as event from '../../../../common/endpoint/models/event'; import { BoldCode } from './panel_content_related_detail'; -import { ResolverEvent } from '../../../../common/endpoint/types'; +import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types'; +import * as selectors from '../../store/selectors'; +import { useResolverDispatch } from '../use_resolver_dispatch'; /** * This view presents a list of related events of a given type for a given process. @@ -22,37 +31,111 @@ import { ResolverEvent } from '../../../../common/endpoint/types'; * | **registry deletion** @ *3:32PM..* *HKLM/software...* | * | **file creation** @ *3:34PM..* *C:/directory/file.exe* | */ + +interface MatchingEventEntry { + formattedDate: string; + eventType: string; + name: string; + entityId: string; + setQueryParams: () => void; +} + +const DisplayList = memo(function DisplayList({ + crumbs, + matchingEventEntries, +}: { + crumbs: Array<{ text: string | JSX.Element; onClick: () => void }>; + matchingEventEntries: MatchingEventEntry[]; +}) { + return ( + <> + + + <> + {matchingEventEntries.map((eventView, index) => { + return ( + <> + + {eventView.eventType} + {' @ '} + {eventView.formattedDate} + + + {eventView.name} + {index === matchingEventEntries.length - 1 ? null : } + + ); + })} + + + ); +}); + export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarrowedByType({ processEvent, eventType, - relatedEventsState, + relatedStats, pushToQueryParams, }: { processEvent: ResolverEvent; pushToQueryParams: (arg0: CrumbInfo) => unknown; eventType: string; - relatedEventsState: RelatedEventDataEntryWithStats; + relatedStats: ResolverNodeStats; }) { const processName = processEvent && event.eventName(processEvent); const processEntityId = event.entityId(processEvent); - const totalCount = Object.values(relatedEventsState.stats).reduce((a, v) => { - return a + v; - }, 0); + const totalCount = relatedStats.events.total; const eventsString = i18n.translate( 'xpack.siem.endpoint.resolver.panel.processEventListByType.events', { defaultMessage: 'Events', } ); + const waitingString = i18n.translate( + 'xpack.siem.endpoint.resolver.panel.processEventListByType.wait', + { + defaultMessage: 'Waiting For Events...', + } + ); + + const relatedsReady = useSelector(selectors.relatedEventsReady).get(processEntityId); + const relatedEventsForThisProcess = useSelector(selectors.relatedEventsByEntityId).get( + processEntityId + ); + const dispatch = useResolverDispatch(); + + useEffect(() => { + if (typeof relatedsReady === 'undefined') { + dispatch({ + type: 'userRequestedRelatedEventData', + payload: processEntityId, + }); + } + }, [relatedsReady, dispatch, processEntityId]); + + const waitCrumbs = useMemo(() => { + return [ + { + text: eventsString, + onClick: () => { + pushToQueryParams({ crumbId: '', crumbEvent: '' }); + }, + }, + ]; + }, [pushToQueryParams, eventsString]); + + const relatedEventsToDisplay = useMemo(() => { + return relatedEventsForThisProcess?.events || []; + }, [relatedEventsForThisProcess?.events]); /** * A list entry will be displayed for each of these */ - const matchingEventEntries = useMemo(() => { - return relatedEventsState.relatedEvents - .reduce((a: ResolverEvent[], { relatedEvent, relatedEventType }) => { - if (relatedEventType === eventType) { - a.push(relatedEvent); + const matchingEventEntries: MatchingEventEntry[] = useMemo(() => { + return relatedEventsToDisplay + .reduce((a: ResolverEvent[], candidate) => { + if (event.ecsEventType(candidate) === eventType) { + a.push(candidate); } return a; }, []) @@ -70,7 +153,7 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr }, }; }); - }, [relatedEventsState, eventType, processEntityId, pushToQueryParams]); + }, [relatedEventsToDisplay, eventType, processEntityId, pushToQueryParams]); const crumbs = useMemo(() => { return [ @@ -116,27 +199,22 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr pushToQueryParams, totalCount, ]); - return ( - <> - - + + /** + * Wait here until the effect resolves... + */ + if (!relatedsReady) { + return ( <> - {matchingEventEntries.map((eventView, index) => { - return ( - <> - - {eventView.eventType} - {' @ '} - {eventView.formattedDate} - - - {eventView.name} - {index === matchingEventEntries.length - 1 ? null : } - - ); - })} + + + +

{waitingString}

+ - - ); + ); + } + + return ; }); ProcessEventListNarrowedByType.displayName = 'ProcessEventListNarrowedByType'; diff --git a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx index ac763150656fd..bcef4815177a1 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx @@ -330,9 +330,9 @@ const ProcessEventDotComponents = React.memo( const handleRelatedEventRequest = useCallback(() => { dispatch({ type: 'userRequestedRelatedEventData', - payload: event, + payload: selfId, }); - }, [dispatch, event]); + }, [dispatch, selfId]); const handleRelatedAlertsRequest = useCallback(() => { dispatch({ @@ -343,7 +343,7 @@ const ProcessEventDotComponents = React.memo( const history = useHistory(); const urlSearch = history.location.search; - + /** * This updates the breadcrumb nav, the table view */ @@ -391,29 +391,36 @@ const ProcessEventDotComponents = React.memo( * generally in the form `number of related events in category` `category title` * e.g. "10 DNS", "230 File" */ - const relatedEventOptions = useMemo(() => { + const { relatedEventOptions, grandTotal } = useMemo(() => { if (!relatedEventsStats) { // Return an empty set of options if there are no stats to report - return []; + return { relatedEventOptions: [], grandTotal: 0 }; } + let runningTotal = 0; // If we have entries to show, map them into options to display in the selectable list - return Object.entries(relatedEventsStats.events.byCategory).map(([category, total]) => { - const displayName = getDisplayName(category); - return { - prefix: , - optionTitle: `${displayName}`, - action: () => { - dispatch({ - type: 'userSelectedRelatedEventCategory', - payload: { - subject: event, - category, + return { + relatedEventOptions: Object.entries(relatedEventsStats.events.byCategory).map( + ([category, total]) => { + const displayName = getDisplayName(category); + runningTotal += total; + return { + prefix: , + optionTitle: `${displayName}`, + action: () => { + dispatch({ + type: 'userSelectedRelatedEventCategory', + payload: { + subject: event, + category, + }, + }); + pushToQueryParams({ crumbId: selfEntityId, crumbEvent: category }); }, - }); - pushToQueryParams({ crumbId: selfEntityId, crumbEvent: category }); - }, - }; - }); + }; + } + ), + grandTotal: runningTotal, + }; }, [relatedEventsStats, dispatch, event, pushToQueryParams, selfEntityId]); const relatedEventStatusOrOptions = (() => { @@ -555,6 +562,7 @@ const ProcessEventDotComponents = React.memo( unknown } & { + }: { menuTitle: string; count?: number; className?: string; menuAction: () => unknown } & { optionsWithActions?: ResolverSubmenuOptionList | string | undefined; }) => { const [menuIsOpen, setMenuOpen] = useState(false); @@ -180,7 +181,8 @@ const NodeSubMenuComponents = React.memo( iconSide="right" tabIndex={-1} > - {menuTitle} + + {` ${menuTitle}`} {menuIsOpen && typeof optionsWithActions === 'object' && ( From ebaa753c8953dddd53e0f46b33645e2b33a4f2b6 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 01:54:32 -0400 Subject: [PATCH 04/78] fix import --- .../security_solution/public/resolver/view/submenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx index 32c478a297fab..f1d0fe5ca2d17 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import React, { ReactNode, useState, useMemo, useCallback, EuiI18nNumber } from 'react'; -import { EuiSelectable, EuiButton } from '@elastic/eui'; +import React, { ReactNode, useState, useMemo, useCallback } from 'react'; +import { EuiSelectable, EuiButton, EuiI18nNumber } from '@elastic/eui'; import styled from 'styled-components'; /** From 867b977ed56798b0de77546c237c68b8bf7de1eb Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 02:34:45 -0400 Subject: [PATCH 05/78] fix import / conflicts --- .../security_solution/public/resolver/view/assets.tsx | 10 ++++++---- .../public/resolver/view/process_event_dot.tsx | 5 ++--- .../security_solution/public/resolver/view/submenu.tsx | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/view/assets.tsx b/x-pack/plugins/security_solution/public/resolver/view/assets.tsx index 9b9250917e193..339915c4ce659 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/assets.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/assets.tsx @@ -13,9 +13,7 @@ import { i18n } from '@kbn/i18n'; import { useUiSetting } from '../../common/lib/kibana'; import { DEFAULT_DARK_MODE } from '../../../common/constants'; import { nodeType } from './process_event_dot'; -import { ResolverEvent } from 'x-pack/plugins/security_solution/common/endpoint/types'; - - +import { ResolverEvent } from '../../../common/endpoint/types'; type ResolverColorNames = | 'descriptionText' @@ -409,7 +407,11 @@ export const SymbolDefinitions = styled(SymbolDefinitionsComponent)` height: 0; `; -export const useResolverTheme = (): { colorMap: ColorMap; nodeAssets: NodeStyleMap, cubeAssetsForNode: (arg0: ResolverEvent)=>NodeStyleConfig } => { +export const useResolverTheme = (): { + colorMap: ColorMap; + nodeAssets: NodeStyleMap; + cubeAssetsForNode: (arg0: ResolverEvent) => NodeStyleConfig; +} => { const isDarkMode = useUiSetting(DEFAULT_DARK_MODE); const theme = isDarkMode ? euiThemeAmsterdamDark : euiThemeAmsterdamLight; diff --git a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx index 6fa7a9804cbd2..2db908cd0091b 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx @@ -259,7 +259,6 @@ const ProcessEventDotComponents = React.memo( // Entity ID of self const selfEntityId = eventModel.entityId(event); - const isShowingEventActions = magFactorX > 0.8; const isShowingDescriptionText = magFactorX >= 0.55; @@ -437,13 +436,13 @@ const ProcessEventDotComponents = React.memo( * generally in the form `number of related events in category` `category title` * e.g. "10 DNS", "230 File" */ - + const [relatedEventOptions, grandTotal] = useMemo(() => { const relatedStatsList = []; if (!relatedEventsStats) { // Return an empty set of options if there are no stats to report - return [[],0]; + return [[], 0]; } let runningTotal = 0; // If we have entries to show, map them into options to display in the selectable list diff --git a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx index 98166850c6c21..e5d8d96bdffdd 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx @@ -110,7 +110,7 @@ const OptionList = React.memo( {...selectableProps} isLoading={isLoading} > - {(list) => {list}} + {(list) => {list}} ); } From 23f8e6a2effa674bce61a6eb2a03b4988a963418 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 02:47:06 -0400 Subject: [PATCH 06/78] fix asset bug with process detail --- .../resolver/view/panels/panel_content_process_detail.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 534e5e266fab4..2c5b8b96b1247 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 @@ -24,9 +24,9 @@ import { md5HashForProcess, argsForProcess, } from '../../models/process_event'; -import { cubeAssetsForNode } from '../process_event_dot'; import { CubeForProcess } from './process_cube_icon'; import { ResolverEvent } from '../../../../common/endpoint/types'; +import { useResolverTheme } from '../assets'; const StyledDescriptionList = styled(EuiDescriptionList)` &.euiDescriptionList.euiDescriptionList--column dt.euiDescriptionList__title.desc-title { @@ -110,6 +110,7 @@ export const ProcessDetails = memo(function ProcessDetails({ }, ]; }, [processName, pushToQueryParams]); + const { cubeAssetsForNode } = useResolverTheme(); const { descriptionText } = useMemo(() => { if (!processEvent) { return { descriptionText: '' }; From b3c542f69341e18defdf38701c43ecaede93dbcc Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 14:18:33 -0400 Subject: [PATCH 07/78] fix conflicts: List related by type --- .../public/resolver/store/data/reducer.ts | 13 +++++++++---- .../public/resolver/store/middleware.ts | 1 + .../view/panels/panel_content_process_detail.tsx | 2 +- .../view/panels/panel_content_related_list.tsx | 9 ++++++--- .../public/resolver/view/process_event_dot.tsx | 4 +++- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts b/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts index 2d50fbf82f8f7..5d57486012892 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts @@ -40,11 +40,16 @@ export const dataReducer: Reducer = (state = initialS }; } else if (action.type === 'userRequestedRelatedEventData') { state.relatedEventsReady.set(action.payload, false); - return state; + return { + ...state, + relatedEventsReady: new Map(state.relatedEventsReady.set(action.payload, false)), + }; } else if (action.type === 'serverReturnedRelatedEventData') { - state.relatedEventsReady.set(action.payload.entityID, true); - state.relatedEvents.set(action.payload.entityID, action.payload); - return state; + return { + ...state, + relatedEventsReady: new Map(state.relatedEventsReady.set(action.payload.entityID, true)), + relatedEvents: new Map(state.relatedEvents.set(action.payload.entityID, action.payload)), + }; } else { return state; } diff --git a/x-pack/plugins/security_solution/public/resolver/store/middleware.ts b/x-pack/plugins/security_solution/public/resolver/store/middleware.ts index e2300b197bf0d..e840f045186d4 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/middleware.ts @@ -103,6 +103,7 @@ export const resolverMiddlewareFactory: MiddlewareFactory = (context) => { query: { events: 100 }, } ); + api.dispatch({ type: 'serverReturnedRelatedEventData', payload: result, 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 2c5b8b96b1247..d51ca04237a95 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 @@ -116,7 +116,7 @@ export const ProcessDetails = memo(function ProcessDetails({ return { descriptionText: '' }; } return cubeAssetsForNode(processEvent); - }, [processEvent]); + }, [processEvent, cubeAssetsForNode]); const titleId = useMemo(() => htmlIdGenerator('resolverTable')(), []); return ( diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index 7f5d9bcc4863a..be6d3e17b96d0 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -98,7 +98,9 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr } ); - const relatedsReady = useSelector(selectors.relatedEventsReady).get(processEntityId); + const relatedsReadyMap = useSelector(selectors.relatedEventsReady); + const relatedsReady = relatedsReadyMap.get(processEntityId); + const relatedEventsForThisProcess = useSelector(selectors.relatedEventsByEntityId).get( processEntityId ); @@ -132,9 +134,9 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr * A list entry will be displayed for each of these */ const matchingEventEntries: MatchingEventEntry[] = useMemo(() => { - return relatedEventsToDisplay + const relateds = relatedEventsToDisplay .reduce((a: ResolverEvent[], candidate) => { - if (event.ecsEventType(candidate) === eventType) { + if (event.eventType(candidate) === eventType) { a.push(candidate); } return a; @@ -153,6 +155,7 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr }, }; }); + return relateds; }, [relatedEventsToDisplay, eventType, processEntityId, pushToQueryParams]); const crumbs = useMemo(() => { diff --git a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx index 2db908cd0091b..fdf1143103387 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx @@ -462,12 +462,14 @@ const ProcessEventDotComponents = React.memo( category, }, }); + + pushToQueryParams({ crumbId: selfEntityId, crumbEvent: category }); }, }); } } return [relatedStatsList, runningTotal]; - }, [relatedEventsStats, dispatch, event]); + }, [relatedEventsStats, dispatch, event, pushToQueryParams, selfEntityId]); const relatedEventStatusOrOptions = (() => { if (!relatedEventsStats) { From 5d096be14a992f51baf10648c4dea6ed66dd3782 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 16:32:57 -0400 Subject: [PATCH 08/78] repair conflicts: related detail --- .../public/resolver/view/panel.tsx | 27 +-- .../view/panels/panel_content_error.tsx | 4 +- .../panels/panel_content_related_detail.tsx | 155 ++++++++++++++---- 3 files changed, 135 insertions(+), 51 deletions(-) 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 e3f0e1c0c5fa9..f7a7676e0f0b5 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -23,6 +23,7 @@ import { ProcessEventListNarrowedByType } from './panels/panel_content_related_l 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'; /** * The two query parameters we read/write on to control which view the table presents: @@ -89,15 +90,17 @@ const PanelContent = memo(function PanelContent() { }, [urlSearch]); const graphableProcesses = useSelector(selectors.graphableProcesses); + const graphableProcessEntityIds = useMemo(() => { + return new Set(graphableProcesses.map(event.entityId)); + }, [graphableProcesses]); // The entity id in query params of a graphable process (or false if none is found) // For 1 case (the related detail, see below), the process id will be in crumbEvent instead of crumbId const idFromParams = useMemo(() => { - const graphableProcessEntityIds = new Set(graphableProcesses.map(event.entityId)); return ( (graphableProcessEntityIds.has(queryParams.crumbId) && queryParams.crumbId) || (graphableProcessEntityIds.has(queryParams.crumbEvent) && queryParams.crumbEvent) ); - }, [graphableProcesses, queryParams]); + }, [queryParams, graphableProcessEntityIds]); // The "selected" node in the tree control. It will sometimes, but not always, correspond with the "active" node const selectedDescendantProcessId = useSelector(selectors.uiSelectedDescendantProcessId); @@ -183,7 +186,6 @@ const PanelContent = memo(function PanelContent() { return ; } - const graphableProcessEntityIds = new Set(graphableProcesses.map(event.entityId)); if (graphableProcessEntityIds.has(crumbId)) { /** * | Crumb/Table | &crumbId | &crumbEvent | @@ -235,26 +237,25 @@ const PanelContent = memo(function PanelContent() { * | :--------------------- | :------------------------- | :---------------------- | * | related event detail | event_id of related event | entity_id of process | */ - // return ( - // - // ); + return ( + + ); } // The default 'Event List' / 'List of all processes' view return ; }, [ - graphableProcesses, uiSelectedEvent, crumbEvent, crumbId, pushToQueryParams, relatedStatsForCrumbId, + graphableProcessEntityIds, ]); return <>{whichTableViewAndBreadcrumbsToRender}; diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx index 530bda0f71fc0..fa5ad03f7d3b5 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx @@ -15,7 +15,7 @@ import { CrumbInfo, StyledBreadcrumbs } from '../panel'; * @param {function} pushToQueryparams A function to update the hash value in the URL to control panel state * @param {string} errorMessage The message to display in the panel when something goes wrong */ -export const TableServiceError = memo(function ({ +export const PanelContentError = memo(function ({ errorMessage, pushToQueryParams, }: { @@ -58,4 +58,4 @@ export const TableServiceError = memo(function ({ ); }); -TableServiceError.displayName = 'TableServiceError'; +PanelContentError.displayName = 'TableServiceError'; 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/panel_content_related_detail.tsx index f69d0a6b7ed3f..45bc79ff0e04f 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/panel_content_related_detail.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { memo, useMemo } from 'react'; +import React, { memo, useMemo, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiI18nNumber, @@ -16,10 +16,13 @@ import { EuiTitle, } from '@elastic/eui'; import styled from 'styled-components'; -import { RelatedEventDataEntryWithStats } from '../../types'; +import { useSelector } from 'react-redux'; import { CrumbInfo, formatDate, StyledBreadcrumbs } from '../panel'; 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'; export const BoldCode = styled(EuiCode)` &.euiCodeBlock code.euiCodeBlock__code { @@ -95,44 +98,77 @@ TitleHr.displayName = 'TitleHR'; * it appears in the underlying ResolverEvent */ export const RelatedEventDetail = memo(function RelatedEventDetail({ - relatedEvent, + relatedEventId, parentEvent, pushToQueryParams, - relatedEventsState, - eventType, + countForParent, }: { - relatedEvent: ResolverEvent; - parentEvent?: ResolverEvent; + relatedEventId: string; + parentEvent: ResolverEvent; pushToQueryParams: (arg0: CrumbInfo) => unknown; - relatedEventsState: RelatedEventDataEntryWithStats; - eventType: string; + countForParent: number | undefined; }) { const processName = (parentEvent && event.eventName(parentEvent)) || '*'; - const processEntityId = event.entityId(relatedEvent); - const totalCount = Object.values(relatedEventsState.stats).reduce((a, v) => { - return a + v; - }, 0); + const processEntityId = parentEvent && event.entityId(parentEvent); + const totalCount = countForParent || 0; const eventsString = i18n.translate( 'xpack.siem.endpoint.resolver.panel.relatedEventDetail.events', { defaultMessage: 'Events', } ); + const naString = i18n.translate('xpack.siem.endpoint.resolver.panel.relatedEventDetail.NA', { + defaultMessage: 'N/A', + }); - const matchingEvents = useMemo(() => { - return relatedEventsState.relatedEvents.reduce( - (matchingSet: ResolverEvent[], { relatedEvent: candidateEvent, relatedEventType }) => { - if (relatedEventType === eventType) { - matchingSet.push(candidateEvent); - } - return matchingSet; - }, - [] + 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: 'userRequestedRelatedEventData', + 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 ); - }, [relatedEventsState, eventType]); + // For breadcrumbs: + const specificCategory = specificEvent && event.eventType(specificEvent); + const countOfCategory = relatedEventsForThisProcess.events.reduce((sumtotal, evt) => { + return event.eventType(evt) === specificCategory ? sumtotal + 1 : sumtotal; + }, 0); + return [specificEvent, countOfCategory, specificCategory || naString]; + }, [relatedEventsForThisProcess, naString, relatedEventId]); const [sections, formattedDate] = useMemo(() => { - const { agent, ecs, process, ...relevantData } = relatedEvent as ResolverEvent & { + 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 & { ecs: unknown; }; let displayDate = ''; @@ -152,7 +188,18 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ }) .filter((v) => v.sectionTitle !== '' && v.entries.length); return [sectionData, displayDate]; - }, [relatedEvent]); + }, [relatedEventToShowDetailsFor, naString]); + + const waitCrumbs = useMemo(() => { + return [ + { + text: eventsString, + onClick: () => { + pushToQueryParams({ crumbId: '', crumbEvent: '' }); + }, + }, + ]; + }, [pushToQueryParams, eventsString]); const crumbs = useMemo(() => { return [ @@ -165,7 +212,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ { text: processName, onClick: () => { - pushToQueryParams({ crumbId: processEntityId, crumbEvent: '' }); + pushToQueryParams({ crumbId: processEntityId!, crumbEvent: '' }); }, }, { @@ -176,47 +223,83 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ ), onClick: () => { - pushToQueryParams({ crumbId: processEntityId, crumbEvent: 'all' }); + pushToQueryParams({ crumbId: processEntityId!, crumbEvent: 'all' }); }, }, { text: ( <> - - {/* Non-breaking space->*/ ` ${eventType}`} + + {/* Non-breaking space->*/ ` ${relatedEventCategory}`} ), onClick: () => { - pushToQueryParams({ crumbId: processEntityId, crumbEvent: eventType }); + pushToQueryParams({ + crumbId: processEntityId!, + crumbEvent: relatedEventCategory || 'all', + }); }, }, { - text: event.descriptiveName(relatedEvent), + text: relatedEventToShowDetailsFor + ? event.descriptiveName(relatedEventToShowDetailsFor) + : naString, onClick: () => {}, }, ]; }, [ processName, processEntityId, - matchingEvents, - eventType, eventsString, pushToQueryParams, - relatedEvent, totalCount, + countBySameCategory, + naString, + relatedEventCategory, + relatedEventToShowDetailsFor, ]); + /** + * If the ship hasn't come in yet, wait on the dock + */ + if (!relatedsReady) { + const waitingString = i18n.translate('xpack.siem.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.siem.endpoint.resolver.panel.relatedDetail.missing', { + defaultMessage: 'Related event not found.', + }); + return ; + } + return ( <> - {`${eventType} ${event.ecsEventType(relatedEvent)}`} + {`${relatedEventCategory} ${event.ecsEventType( + relatedEventToShowDetailsFor + )}`} {' @ '} {formattedDate} - {event.descriptiveName(relatedEvent)} + {event.descriptiveName(relatedEventToShowDetailsFor)} {sections.map(({ sectionTitle, entries }, index) => { return ( From 10ee8ecdce56563668ffe49bfebef15707b9618c Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 16:41:34 -0400 Subject: [PATCH 09/78] style conflict --- .../security_solution/public/resolver/view/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/view/index.tsx b/x-pack/plugins/security_solution/public/resolver/view/index.tsx index 0e15cd5c4e1da..9dfc9a45fafeb 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/index.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/index.tsx @@ -53,9 +53,9 @@ const StyledResolver = styled.div` const StyledPanel = styled(Panel)` position: absolute; - left: 1em; - top: 1em; - max-height: calc(100% - 2em); + left: 0; + top: 0; + bottom: 0; overflow: auto; width: 25em; max-width: 50%; From 0fa1f07da06304b6af2a66169d0bb222d796b18d Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 17:15:29 -0400 Subject: [PATCH 10/78] style touchups --- .../public/resolver/view/panel.tsx | 18 +++++++++++------- .../panels/panel_content_related_detail.tsx | 10 ++++++---- 2 files changed, 17 insertions(+), 11 deletions(-) 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 f7a7676e0f0b5..d95490f77644a 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -167,9 +167,12 @@ const PanelContent = memo(function PanelContent() { const relatedEventStats = useSelector(selectors.relatedEventsStats); const { crumbId, crumbEvent } = queryParams; - const relatedStatsForCrumbId = useMemo(() => { - return relatedEventStats.get(crumbId); - }, [relatedEventStats, crumbId]); + const relatedStatsForIdFromParams = useMemo(() => { + if(idFromParams){ + return relatedEventStats.get(idFromParams); + } + return undefined; + }, [relatedEventStats, idFromParams]); /** * Determine which set of breadcrumbs to display based on the query parameters @@ -209,7 +212,7 @@ const PanelContent = memo(function PanelContent() { ); } @@ -225,12 +228,13 @@ const PanelContent = memo(function PanelContent() { ); } } + if (graphableProcessEntityIds.has(crumbEvent)) { /** * | Crumb/Table | &crumbId | &crumbEvent | @@ -242,7 +246,7 @@ const PanelContent = memo(function PanelContent() { relatedEventId={crumbId} parentEvent={uiSelectedEvent!} pushToQueryParams={pushToQueryParams} - countForParent={relatedStatsForCrumbId?.events.total} + countForParent={relatedStatsForIdFromParams?.events.total} /> ); } @@ -254,7 +258,7 @@ const PanelContent = memo(function PanelContent() { crumbEvent, crumbId, pushToQueryParams, - relatedStatsForCrumbId, + relatedStatsForIdFromParams, graphableProcessEntityIds, ]); 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/panel_content_related_detail.tsx index 45bc79ff0e04f..80a06d34a25cf 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/panel_content_related_detail.tsx @@ -74,9 +74,10 @@ const StyledDescriptionList = memo(styled(EuiDescriptionList)` `); // Styling subtitles, per UX review: -const StyledFlexTitle = memo(styled('h4')` +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 { @@ -291,7 +292,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ <> - + {`${relatedEventCategory} ${event.ecsEventType( relatedEventToShowDetailsFor )}`} @@ -304,8 +305,9 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ {sections.map(({ sectionTitle, entries }, index) => { return ( <> + {index === 0 ? null : } - + {sectionTitle} @@ -319,7 +321,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ compressed listItems={entries} /> - {index === sections.length - 1 ? null : } + {index === sections.length - 1 ? null : } ); })} From 3d16dabba391bf34c2e8f31fc90c200e7781ce94 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 17:33:47 -0400 Subject: [PATCH 11/78] add counts --- .../security_solution/public/resolver/view/submenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx index e5d8d96bdffdd..438bae8fc6fd5 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import React, { ReactNode, useState, useMemo, useCallback } from 'react'; -import { EuiSelectable, EuiButton, EuiPopover, ButtonColor, htmlIdGenerator } from '@elastic/eui'; +import { EuiI18nNumber, EuiSelectable, EuiButton, EuiPopover, ButtonColor, htmlIdGenerator } from '@elastic/eui'; import styled from 'styled-components'; /** @@ -201,7 +201,7 @@ const NodeSubMenuComponents = React.memo( iconSide="right" tabIndex={-1} > - {menuTitle} + {count ? : ''} {menuTitle} ); From 23837dc02c913e0b65ae60d9ad91fb720e66d3ea Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 17:36:41 -0400 Subject: [PATCH 12/78] linting --- .../security_solution/public/resolver/view/panel.tsx | 2 +- .../security_solution/public/resolver/view/submenu.tsx | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) 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 d95490f77644a..03c614035a084 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -168,7 +168,7 @@ const PanelContent = memo(function PanelContent() { const relatedEventStats = useSelector(selectors.relatedEventsStats); const { crumbId, crumbEvent } = queryParams; const relatedStatsForIdFromParams = useMemo(() => { - if(idFromParams){ + if (idFromParams) { return relatedEventStats.get(idFromParams); } return undefined; diff --git a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx index 438bae8fc6fd5..0dbb6d22ca3b7 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx @@ -6,7 +6,14 @@ import { i18n } from '@kbn/i18n'; import React, { ReactNode, useState, useMemo, useCallback } from 'react'; -import { EuiI18nNumber, EuiSelectable, EuiButton, EuiPopover, ButtonColor, htmlIdGenerator } from '@elastic/eui'; +import { + EuiI18nNumber, + EuiSelectable, + EuiButton, + EuiPopover, + ButtonColor, + htmlIdGenerator, +} from '@elastic/eui'; import styled from 'styled-components'; /** From 8f6a90391dd91c22ec5910313077425dc6cf228e Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 18:42:46 -0400 Subject: [PATCH 13/78] delete unused view --- .../view/panels/panel_content_wait.tsx | 59 ------------------- 1 file changed, 59 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_wait.tsx diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_wait.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_wait.tsx deleted file mode 100644 index 6b60ab2e20c2e..0000000000000 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_wait.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 { i18n } from '@kbn/i18n'; -import React, { memo, useEffect, useMemo } from 'react'; -import { EuiText, EuiLoadingSpinner } from '@elastic/eui'; -import { useResolverDispatch } from '../use_resolver_dispatch'; -import { ResolverEvent } from '../../../../common/endpoint/types'; -import { StyledBreadcrumbs } from '../panel'; - -/** - * Display a waiting message to the user when we can't display what they requested because we don't have related event data yet. - * If the related event data has not been requested yet (reflected by `relatedEventsState` being undefined) then issue a request. - */ -export const WaitForRelatedEvents = memo(function ({ - processEvent, - relatedEventsState, -}: { - processEvent: ResolverEvent; - relatedEventsState: 'waitingForRelatedEventData' | undefined; -}) { - const dispatch = useResolverDispatch(); - useEffect(() => { - if (processEvent && relatedEventsState !== 'waitingForRelatedEventData') { - // Don't request again if it's already waiting - dispatch({ - type: 'userRequestedRelatedEventData', - payload: processEvent, - }); - } - }, [dispatch, processEvent, relatedEventsState]); - const crumbs = useMemo(() => { - return [ - { - text: i18n.translate('xpack.siem.endpoint.resolver.panel.waiting.events', { - defaultMessage: 'Events', - }), - onClick: () => {}, - }, - ]; - }, []); - return ( - <> - - -
- -
- {i18n.translate('xpack.siem.endpoint.resolver.panel.waiting.waiting', { - defaultMessage: 'Waiting For Related Events...', - })} -
- - ); -}); -WaitForRelatedEvents.displayName = 'WaitForRelatedEvents'; From 74e9bec3d119cb11faf90d287d65459bacf78193 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 19:26:42 -0400 Subject: [PATCH 14/78] fixing deps --- .../public/resolver/view/assets.tsx | 20 ++++++- .../public/resolver/view/panel.tsx | 44 +--------------- .../view/panels/panel_content_error.tsx | 2 +- .../panels/panel_content_process_detail.tsx | 2 +- .../panels/panel_content_process_list.tsx | 2 +- .../panels/panel_content_related_counts.tsx | 2 +- .../panels/panel_content_related_detail.tsx | 2 +- .../view/panels/panel_content_utilities.tsx | 52 +++++++++++++++++++ .../resolver/view/process_event_dot.tsx | 24 ++------- 9 files changed, 81 insertions(+), 69 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx diff --git a/x-pack/plugins/security_solution/public/resolver/view/assets.tsx b/x-pack/plugins/security_solution/public/resolver/view/assets.tsx index 339915c4ce659..ef65f11575dd1 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/assets.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/assets.tsx @@ -12,8 +12,9 @@ import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { useUiSetting } from '../../common/lib/kibana'; import { DEFAULT_DARK_MODE } from '../../../common/constants'; -import { nodeType } from './process_event_dot'; import { ResolverEvent } from '../../../common/endpoint/types'; +import * as processModel from '../models/process_event'; +import { ResolverProcessType } from '../types'; type ResolverColorNames = | 'descriptionText' @@ -407,6 +408,23 @@ export const SymbolDefinitions = styled(SymbolDefinitionsComponent)` height: 0; `; +const processTypeToCube: Record = { + processCreated: 'runningProcessCube', + processRan: 'runningProcessCube', + processTerminated: 'terminatedProcessCube', + unknownProcessEvent: 'runningProcessCube', + processCausedAlert: 'runningTriggerCube', + unknownEvent: 'runningProcessCube', +}; + +export function nodeType(processEvent: ResolverEvent): keyof NodeStyleMap { + const processType = processModel.eventType(processEvent); + if (processType in processTypeToCube) { + return processTypeToCube[processType]; + } + return 'runningProcessCube'; +} + export const useResolverTheme = (): { colorMap: ColorMap; nodeAssets: NodeStyleMap; 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 03c614035a084..1f3f1b70f2b32 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -6,13 +6,10 @@ import React, { memo, useCallback, useMemo, useContext, useLayoutEffect, useState } from 'react'; import { useSelector } from 'react-redux'; -import { i18n } from '@kbn/i18n'; import { useHistory } from 'react-router-dom'; // eslint-disable-next-line import/no-nodejs-modules import querystring from 'querystring'; - -import { EuiPanel, EuiBreadcrumbs } from '@elastic/eui'; -import styled from 'styled-components'; +import { EuiPanel } from '@elastic/eui'; import { displayNameRecord } from './process_event_dot'; import * as selectors from '../store/selectors'; import { useResolverDispatch } from './use_resolver_dispatch'; @@ -24,46 +21,9 @@ 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 { CrumbInfo } from './panels/panel_content_utilities'; -/** - * The two query parameters we read/write on to control which view the table presents: - */ -export interface CrumbInfo { - readonly crumbId: string; - readonly crumbEvent: string; -} - -/** - * Breadcrumb menu with adjustments per direction from UX team - */ -export const StyledBreadcrumbs = styled(EuiBreadcrumbs)` - &.euiBreadcrumbs.euiBreadcrumbs--responsive { - background-color: #f5f5fa; - padding: 1em; - } -`; - -export const formatter = new Intl.DateTimeFormat(i18n.getLocale(), { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', -}); -/** - * @param {ConstructorParameters[0]} timestamp To be passed through Date->Intl.DateTimeFormat - * @returns {string} A nicely formatted string for a date - */ -export function formatDate(timestamp: ConstructorParameters[0]) { - const date = new Date(timestamp); - if (isFinite(date.getTime())) { - return formatter.format(date); - } else { - return 'Invalid Date'; - } -} /** * The team decided to use this determinant to express how we comport state in the UI with the values of the two query params: diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx index fa5ad03f7d3b5..aa93a83649eee 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { EuiSpacer, EuiText, EuiButtonEmpty } from '@elastic/eui'; import React, { memo, useMemo } from 'react'; -import { CrumbInfo, StyledBreadcrumbs } from '../panel'; +import { CrumbInfo, StyledBreadcrumbs } from './panel_content_utilities'; /** * Display an error in the panel when something goes wrong and give the user a way to "retreat" back to a default state. 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 d51ca04237a95..8cf073ecaa335 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 @@ -15,7 +15,7 @@ import { } from '@elastic/eui'; import styled from 'styled-components'; import * as event from '../../../../common/endpoint/models/event'; -import { CrumbInfo, formatDate, StyledBreadcrumbs } from '../panel'; +import { CrumbInfo, formatDate, StyledBreadcrumbs } from './panel_content_utilities'; import { hostPathForProcess, hostPidForProcess, 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 c5e893dcd786c..4864d30ab126f 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 @@ -15,7 +15,7 @@ import { i18n } from '@kbn/i18n'; import { useSelector } from 'react-redux'; import * as event from '../../../../common/endpoint/models/event'; import * as selectors from '../../store/selectors'; -import { CrumbInfo, formatter, StyledBreadcrumbs } from '../panel'; +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'; 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/panel_content_related_counts.tsx index c846220278baf..c7aaf9897b832 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/panel_content_related_counts.tsx @@ -13,7 +13,7 @@ import { EuiSpacer, EuiInMemoryTable, } from '@elastic/eui'; -import { CrumbInfo, StyledBreadcrumbs } from '../panel'; +import { CrumbInfo, StyledBreadcrumbs } from './panel_content_utilities'; import * as event from '../../../../common/endpoint/models/event'; import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types'; 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/panel_content_related_detail.tsx index 80a06d34a25cf..94924079d7958 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/panel_content_related_detail.tsx @@ -17,7 +17,7 @@ import { } from '@elastic/eui'; import styled from 'styled-components'; import { useSelector } from 'react-redux'; -import { CrumbInfo, formatDate, StyledBreadcrumbs } from '../panel'; +import { CrumbInfo, formatDate, StyledBreadcrumbs } from './panel_content_utilities'; import * as event from '../../../../common/endpoint/models/event'; import { ResolverEvent } from '../../../../common/endpoint/types'; import * as selectors from '../../store/selectors'; diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx new file mode 100644 index 0000000000000..97e3a65793506 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx @@ -0,0 +1,52 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { EuiBreadcrumbs } from '@elastic/eui'; +import styled from 'styled-components'; + +/** + * The two query parameters we read/write on to control which view the table presents: + */ +export interface CrumbInfo { + readonly crumbId: string; + readonly crumbEvent: string; + } + + /** + * Breadcrumb menu with adjustments per direction from UX team + */ + export const StyledBreadcrumbs = styled(EuiBreadcrumbs)` + &.euiBreadcrumbs.euiBreadcrumbs--responsive { + background-color: #f5f5fa; + padding: 1em; + } + `; + + /** + * Long formatter (to second) for DateTime + */ + export const formatter = new Intl.DateTimeFormat(i18n.getLocale(), { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }); + +/** + * @param {ConstructorParameters[0]} timestamp To be passed through Date->Intl.DateTimeFormat + * @returns {string} A nicely formatted string for a date + */ +export function formatDate(timestamp: ConstructorParameters[0]) { + const date = new Date(timestamp); + if (isFinite(date.getTime())) { + return formatter.format(date); + } else { + return 'Invalid Date'; + } + } diff --git a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx index fdf1143103387..bdc76f0b62902 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx @@ -21,14 +21,13 @@ import { useHistory } from 'react-router-dom'; import querystring from 'querystring'; import { NodeSubMenu, subMenuAssets } from './submenu'; import { applyMatrix3 } from '../lib/vector2'; -import { Vector2, Matrix3, AdjacentProcessMap, ResolverProcessType } from '../types'; -import { SymbolIds, useResolverTheme, NodeStyleMap, calculateResolverFontSize } from './assets'; +import { Vector2, Matrix3, AdjacentProcessMap } from '../types'; +import { SymbolIds, useResolverTheme, calculateResolverFontSize, nodeType } from './assets'; import { ResolverEvent, ResolverNodeStats } from '../../../common/endpoint/types'; import { useResolverDispatch } from './use_resolver_dispatch'; import * as eventModel from '../../../common/endpoint/models/event'; -import * as processModel from '../models/process_event'; import * as selectors from '../store/selectors'; -import { CrumbInfo } from './panel'; +import { CrumbInfo } from './panels/panel_content_utilities'; /** * A map of all known event types (in ugly schema format) to beautifully i18n'd display names @@ -686,20 +685,3 @@ export const ProcessEventDot = styled(ProcessEventDotComponents)` color: white; } `; - -const processTypeToCube: Record = { - processCreated: 'runningProcessCube', - processRan: 'runningProcessCube', - processTerminated: 'terminatedProcessCube', - unknownProcessEvent: 'runningProcessCube', - processCausedAlert: 'runningTriggerCube', - unknownEvent: 'runningProcessCube', -}; - -export function nodeType(processEvent: ResolverEvent): keyof NodeStyleMap { - const processType = processModel.eventType(processEvent); - if (processType in processTypeToCube) { - return processTypeToCube[processType]; - } - return 'runningProcessCube'; -} From 680b152919a074ef88b7f1a858cf914b8260f9d5 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 19:27:42 -0400 Subject: [PATCH 15/78] lint --- .../public/resolver/view/panel.tsx | 2 - .../view/panels/panel_content_utilities.tsx | 60 +++++++++---------- 2 files changed, 30 insertions(+), 32 deletions(-) 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 1f3f1b70f2b32..830273787d64f 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -23,8 +23,6 @@ import { ProcessListWithCounts } from './panels/panel_content_process_list'; import { RelatedEventDetail } from './panels/panel_content_related_detail'; import { CrumbInfo } from './panels/panel_content_utilities'; - - /** * The team decided to use this determinant to express how we comport state in the UI with the values of the two query params: * diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx index 97e3a65793506..6254bc77373b5 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx @@ -12,41 +12,41 @@ import styled from 'styled-components'; * The two query parameters we read/write on to control which view the table presents: */ export interface CrumbInfo { - readonly crumbId: string; - readonly crumbEvent: string; + readonly crumbId: string; + readonly crumbEvent: string; +} + +/** + * Breadcrumb menu with adjustments per direction from UX team + */ +export const StyledBreadcrumbs = styled(EuiBreadcrumbs)` + &.euiBreadcrumbs.euiBreadcrumbs--responsive { + background-color: #f5f5fa; + padding: 1em; } - - /** - * Breadcrumb menu with adjustments per direction from UX team - */ - export const StyledBreadcrumbs = styled(EuiBreadcrumbs)` - &.euiBreadcrumbs.euiBreadcrumbs--responsive { - background-color: #f5f5fa; - padding: 1em; - } - `; - - /** - * Long formatter (to second) for DateTime - */ - export const formatter = new Intl.DateTimeFormat(i18n.getLocale(), { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - }); +`; + +/** + * Long formatter (to second) for DateTime + */ +export const formatter = new Intl.DateTimeFormat(i18n.getLocale(), { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', +}); /** * @param {ConstructorParameters[0]} timestamp To be passed through Date->Intl.DateTimeFormat * @returns {string} A nicely formatted string for a date */ export function formatDate(timestamp: ConstructorParameters[0]) { - const date = new Date(timestamp); - if (isFinite(date.getTime())) { - return formatter.format(date); - } else { - return 'Invalid Date'; - } + const date = new Date(timestamp); + if (isFinite(date.getTime())) { + return formatter.format(date); + } else { + return 'Invalid Date'; } +} From 67eb433050366a92e81941c9a71740fe633d52b4 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 20:14:20 -0400 Subject: [PATCH 16/78] that wasn't really a selector --- .../endpoint_alerts/store/middleware.ts | 22 +++++++++++++++++-- .../public/endpoint_alerts/store/selectors.ts | 22 ------------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts b/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts index 19bf516ce85ca..0d6cf718a62cd 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts @@ -18,11 +18,29 @@ import { isOnAlertPage, apiQueryParams, uiQueryParams, - selectedAlertHasChanged, hasSelectedAlert, isAlertPageTabChange, } from './selectors'; +let lastSelectedAlert: string | null = null; +/** + * @returns true once per change of `selectedAlert` in query params. + * + * As opposed to `hasSelectedAlert` which always returns true if the alert is present + * query params, which can cause unnecessary requests and re-renders in some cases. + */ +const selectedAlertHasChanged = (params: ReturnType): boolean => { + const { selected_alert: selectedAlert } = params; + if (typeof selectedAlert !== 'string') { + return false; + } + if (selectedAlert === lastSelectedAlert) { + return false; + } + lastSelectedAlert = selectedAlert; + return true; +}; + export const alertMiddlewareFactory: ImmutableMiddlewareFactory = ( coreStart, depsStart @@ -55,7 +73,7 @@ export const alertMiddlewareFactory: ImmutableMiddlewareFactory }); api.dispatch({ type: 'serverReturnedAlertsData', payload: listResponse }); - if (hasSelectedAlert(state) && selectedAlertHasChanged(state)) { + if (hasSelectedAlert(state) && selectedAlertHasChanged(uiQueryParams(state))) { const uiParams = uiQueryParams(state); const detailsResponse: AlertDetails = await coreStart.http.get( `/api/endpoint/alerts/${uiParams.selected_alert}` diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/store/selectors.ts b/x-pack/plugins/security_solution/public/endpoint_alerts/store/selectors.ts index c89e08edd4040..ab0e4165a2577 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/store/selectors.ts @@ -188,25 +188,3 @@ export const selectedAlertDetailsTabId: ( uiQueryParams, ({ active_details_tab: activeDetailsTab }) => activeDetailsTab ); - -let lastSelectedAlert: string | null = null; -/** - * @returns true once per change of `selectedAlert` in query params. - * - * As opposed to `hasSelectedAlert` which always returns true if the alert is present - * query params, which can cause unnecessary requests and re-renders in some cases. - */ -export const selectedAlertHasChanged: ( - state: Immutable -) => boolean = createSelector(uiQueryParams, function hasChanged({ - selected_alert: selectedAlert, -}) { - if (typeof selectedAlert !== 'string') { - return false; - } - if (selectedAlert === lastSelectedAlert) { - return false; - } - lastSelectedAlert = selectedAlert; - return true; -}); From f13aa669134e97958b6f87affc6d6c1ad48d199b Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 20:42:35 -0400 Subject: [PATCH 17/78] comments and fixes --- .../public/resolver/view/panel.tsx | 19 +++++++++++++------ .../panels/panel_content_related_list.tsx | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) 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 830273787d64f..f88748aa9b56c 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -60,13 +60,16 @@ const PanelContent = memo(function PanelContent() { ); }, [queryParams, graphableProcessEntityIds]); - // The "selected" node in the tree control. It will sometimes, but not always, correspond with the "active" node + // The "selected" node (and its corresponding event) in the tree control. + // It may need to be synchronized with the ID indicated as selected via the `idFromParams` + // memo above. When this is the case, it is handled by the layout effect below. const selectedDescendantProcessId = useSelector(selectors.uiSelectedDescendantProcessId); const uiSelectedEvent = useMemo(() => { return graphableProcesses.find((evt) => event.entityId(evt) === selectedDescendantProcessId); }, [graphableProcesses, selectedDescendantProcessId]); - // Until an event is dispatched during update, the event indicated as selected by params may be different than the one in state + // Until an event is dispatched during update, the event indicated as selected by params may + // be different than the one in state. const paramsSelectedEvent = useMemo(() => { return graphableProcesses.find((evt) => event.entityId(evt) === idFromParams); }, [graphableProcesses, idFromParams]); @@ -75,8 +78,9 @@ const PanelContent = memo(function PanelContent() { /** * When the ui-selected node is _not_ the one indicated by the query params, but the id from params _is_ in the current tree, - * dispatch a selection action to repair the UI to hold the query id as "selected". - * This is to cover cases where users e.g. share links to reconstitute a Resolver state and it _should never run otherwise_ under the assumption that the query parameters are updated along with the selection in state + * dispatch a selection action to amend the UI state to hold the query id as "selected". + * This is to cover cases where users e.g. share links to reconstitute a Resolver state or + * an effect pushes a new process id to the query params. */ useLayoutEffect(() => { if ( @@ -97,7 +101,8 @@ const PanelContent = memo(function PanelContent() { }, [dispatch, uiSelectedEvent, paramsSelectedEvent, lastUpdatedProcess, timestamp]); /** - * This updates the breadcrumb nav, the table view + * This updates the breadcrumb nav and the panel view. It's supplied to each + * panel content view to allow them to dispatch transitions to each other. */ const pushToQueryParams = useCallback( (newCrumbs: CrumbInfo) => { @@ -117,12 +122,14 @@ const PanelContent = memo(function PanelContent() { } const relativeURL = { search: querystring.stringify(crumbsToPass) }; - + // We probably don't want to nuke the user's history with a huge + // trail of these, thus `.replace` instead of `.push` return history.replace(relativeURL); }, [history, urlSearch] ); + // GO JONNY GO const relatedEventStats = useSelector(selectors.relatedEventsStats); const { crumbId, crumbEvent } = queryParams; const relatedStatsForIdFromParams = useMemo(() => { diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index be6d3e17b96d0..257e4b3fcae33 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -15,7 +15,7 @@ import { EuiHorizontalRule, } from '@elastic/eui'; import { useSelector } from 'react-redux'; -import { CrumbInfo, formatDate, StyledBreadcrumbs } from '../panel'; +import { CrumbInfo, formatDate, StyledBreadcrumbs } from './panel_content_utilities'; import * as event from '../../../../common/endpoint/models/event'; import { BoldCode } from './panel_content_related_detail'; import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types'; From e3047dc266c6d71e8e35520162449defde014c5b Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 21:48:55 -0400 Subject: [PATCH 18/78] theming adjustments --- .../view/panels/panel_content_utilities.tsx | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx index 6254bc77373b5..93cff31d7c56c 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx @@ -5,8 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { EuiBreadcrumbs } from '@elastic/eui'; +import { EuiBreadcrumbs, Breadcrumb } from '@elastic/eui'; import styled from 'styled-components'; +import React, { memo } from 'react'; +import { useResolverTheme } from '../assets'; /** * The two query parameters we read/write on to control which view the table presents: @@ -16,16 +18,37 @@ export interface CrumbInfo { readonly crumbEvent: string; } -/** - * Breadcrumb menu with adjustments per direction from UX team - */ -export const StyledBreadcrumbs = styled(EuiBreadcrumbs)` +const ThemedBreadcrumbs = styled(EuiBreadcrumbs)<{ crumbsBackground: string; crumbText: string }>` &.euiBreadcrumbs.euiBreadcrumbs--responsive { - background-color: #f5f5fa; + background-color: ${(props) => props.crumbsBackground}; + color: ${(props) => props.crumbText}; padding: 1em; } `; +/** + * Breadcrumb menu with adjustments per direction from UX team + */ +export const StyledBreadcrumbs = memo(function StyledBreadcrumbs({ + breadcrumbs, + truncate, +}: { + breadcrumbs: Breadcrumb[]; + truncate?: boolean; +}) { + const { + colorMap: { resolverEdge, resolverEdgeText }, + } = useResolverTheme(); + return ( + + ); +}); + /** * Long formatter (to second) for DateTime */ From e5ea5afdad453ab7e1cd074f464c3abc46dfce76 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 22:09:01 -0400 Subject: [PATCH 19/78] consistent counts --- .../security_solution/public/resolver/view/panel.tsx | 6 +++++- .../resolver/view/panels/panel_content_related_counts.tsx | 5 ++++- .../resolver/view/panels/panel_content_related_list.tsx | 5 ++++- 3 files changed, 13 insertions(+), 3 deletions(-) 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 f88748aa9b56c..73dd728f7ffa3 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -206,12 +206,16 @@ const PanelContent = memo(function PanelContent() { * | :--------------------- | :------------------------- | :---------------------- | * | related event detail | event_id of related event | entity_id of process | */ + const parentCount: number = Object.values(relatedStatsForIdFromParams || {}).reduce( + (sum, val) => sum + val, + 0 + ); return ( ); } 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/panel_content_related_counts.tsx index c7aaf9897b832..2923369cd9ee1 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/panel_content_related_counts.tsx @@ -46,7 +46,10 @@ export const EventCountsForProcess = memo(function EventCountsForProcess({ const relatedEventsState = { stats: relatedStats.events.byCategory }; const processName = processEvent && event.eventName(processEvent); const processEntityId = event.entityId(processEvent); - const totalCount = relatedStats.events.total; + const totalCount = Object.values(relatedStats.events.byCategory).reduce( + (sum, val) => sum + val, + 0 + ); const eventsString = i18n.translate( 'xpack.siem.endpoint.resolver.panel.processEventCounts.events', { diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index 257e4b3fcae33..b6e31c4539e84 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -84,7 +84,10 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr }) { const processName = processEvent && event.eventName(processEvent); const processEntityId = event.entityId(processEvent); - const totalCount = relatedStats.events.total; + const totalCount = Object.values(relatedStats.events.byCategory).reduce( + (sum, val) => sum + val, + 0 + ); const eventsString = i18n.translate( 'xpack.siem.endpoint.resolver.panel.processEventListByType.events', { From 3f7a70f00c769747d59488525ad6daefb123df59 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 22:12:08 -0400 Subject: [PATCH 20/78] lint --- x-pack/plugins/security_solution/public/resolver/view/panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 73dd728f7ffa3..694833d0315dd 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -206,7 +206,7 @@ const PanelContent = memo(function PanelContent() { * | :--------------------- | :------------------------- | :---------------------- | * | related event detail | event_id of related event | entity_id of process | */ - const parentCount: number = Object.values(relatedStatsForIdFromParams || {}).reduce( + const parentCount: number = Object.values(relatedStatsForIdFromParams?.events.byCategory || {}).reduce( (sum, val) => sum + val, 0 ); From e618094f11446f9e5a242242d7f4f84e9b29e62d Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sat, 13 Jun 2020 23:30:53 -0400 Subject: [PATCH 21/78] i18n domain fixes --- .../public/resolver/view/panel.tsx | 7 +- .../view/panels/panel_content_error.tsx | 6 +- .../panels/panel_content_process_detail.tsx | 41 +++--- .../panels/panel_content_process_list.tsx | 31 +++-- .../panels/panel_content_related_counts.tsx | 6 +- .../panels/panel_content_related_detail.tsx | 29 +++-- .../panels/panel_content_related_list.tsx | 4 +- .../resolver/view/process_event_dot.tsx | 119 ++++++++++-------- 8 files changed, 145 insertions(+), 98 deletions(-) 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 694833d0315dd..899691b892630 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -206,10 +206,9 @@ const PanelContent = memo(function PanelContent() { * | :--------------------- | :------------------------- | :---------------------- | * | related event detail | event_id of related event | entity_id of process | */ - const parentCount: number = Object.values(relatedStatsForIdFromParams?.events.byCategory || {}).reduce( - (sum, val) => sum + val, - 0 - ); + const parentCount: number = Object.values( + relatedStatsForIdFromParams?.events.byCategory || {} + ).reduce((sum, val) => sum + val, 0); return ( { return [ { - text: i18n.translate('xpack.siem.endpoint.resolver.panel.error.events', { + text: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.error.events', { defaultMessage: 'Events', }), onClick: () => { @@ -33,7 +33,7 @@ export const PanelContentError = memo(function ({ }, }, { - text: i18n.translate('xpack.siem.endpoint.resolver.panel.error.error', { + text: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.error.error', { defaultMessage: 'Error', }), onClick: () => {}, @@ -51,7 +51,7 @@ export const PanelContentError = memo(function ({ pushToQueryParams({ crumbId: '', crumbEvent: '' }); }} > - {i18n.translate('xpack.siem.endpoint.resolver.panel.error.goBack', { + {i18n.translate('xpack.securitySolution.enpoint.resolver.panel.error.goBack', { defaultMessage: 'Click this link to return to the list of all processes.', })} 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 8cf073ecaa335..e9064ff305629 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 @@ -55,30 +55,36 @@ export const ProcessDetails = memo(function ProcessDetails({ const processInfo = processEvent ? { - [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.created', { + [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.created', { defaultMessage: 'Created', })]: dateTime, - [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.path', { + [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.path', { defaultMessage: 'Path', })]: hostPathForProcess(processEvent), - [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.pid', { + [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.pid', { defaultMessage: 'PID', })]: hostPidForProcess(processEvent), - [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.user', { + [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.user', { defaultMessage: 'User', })]: (userInfoForProcess(processEvent) as { name: string; domain: string }).name, - [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.domain', { + [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.domain', { defaultMessage: 'Domain', - [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.parentPid', { - defaultMessage: 'Parent PID', - })]: hostParentPidForProcess(processEvent), + [i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.processDescList.parentPid', + { + defaultMessage: 'Parent PID', + } + )]: hostParentPidForProcess(processEvent), })]: (userInfoForProcess(processEvent) as { name: string; domain: string }).domain, - [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.md5hash', { + [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.md5hash', { defaultMessage: 'MD5', })]: md5HashForProcess(processEvent), - [i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.commandLine', { - defaultMessage: 'Command Line', - })]: argsForProcess(processEvent), + [i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.processDescList.commandLine', + { + defaultMessage: 'Command Line', + } + )]: argsForProcess(processEvent), } : {}; @@ -94,16 +100,19 @@ export const ProcessDetails = memo(function ProcessDetails({ const crumbs = useMemo(() => { return [ { - text: i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.events', { - defaultMessage: 'Events', - }), + text: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.processDescList.events', + { + defaultMessage: 'Events', + } + ), onClick: () => { pushToQueryParams({ crumbId: '', crumbEvent: '' }); }, }, { text: - i18n.translate('xpack.siem.endpoint.resolver.panel.processDescList.details', { + i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.details', { defaultMessage: 'Details for: ', }) + processName, onClick: () => {}, 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 4864d30ab126f..bf23deac7c90a 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 @@ -57,16 +57,19 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({ () => [ { field: 'name', - name: i18n.translate('xpack.siem.endpoint.resolver.panel.table.row.processNameTitle', { - defaultMessage: 'Process Name', - }), + name: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.table.row.processNameTitle', + { + defaultMessage: 'Process Name', + } + ), sortable: true, truncateText: true, render(name: string, item: ProcessTableView) { return name === '' ? ( {i18n.translate( - 'xpack.siem.endpoint.resolver.panel.table.row.valueMissingDescription', + 'xpack.securitySolution.enpoint.resolver.panel.table.row.valueMissingDescription', { defaultMessage: 'Value is missing', } @@ -87,9 +90,12 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({ }, { field: 'timestamp', - name: i18n.translate('xpack.siem.endpoint.resolver.panel.table.row.timestampTitle', { - defaultMessage: 'Timestamp', - }), + name: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.table.row.timestampTitle', + { + defaultMessage: 'Timestamp', + } + ), dataType: 'date', sortable: true, render(eventDate?: Date) { @@ -98,7 +104,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({ ) : ( {i18n.translate( - 'xpack.siem.endpoint.resolver.panel.table.row.timestampInvalidLabel', + 'xpack.securitySolution.enpoint.resolver.panel.table.row.timestampInvalidLabel', { defaultMessage: 'invalid', } @@ -136,9 +142,12 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({ const crumbs = useMemo(() => { return [ { - text: i18n.translate('xpack.siem.endpoint.resolver.panel.processListWithCounts.events', { - defaultMessage: 'All Process Events', - }), + text: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.processListWithCounts.events', + { + defaultMessage: 'All Process Events', + } + ), onClick: () => {}, }, ]; 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/panel_content_related_counts.tsx index 2923369cd9ee1..409f849529517 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/panel_content_related_counts.tsx @@ -51,7 +51,7 @@ export const EventCountsForProcess = memo(function EventCountsForProcess({ 0 ); const eventsString = i18n.translate( - 'xpack.siem.endpoint.resolver.panel.processEventCounts.events', + 'xpack.securitySolution.enpoint.resolver.panel.processEventCounts.events', { defaultMessage: 'Events', } @@ -97,7 +97,7 @@ export const EventCountsForProcess = memo(function EventCountsForProcess({ () => [ { field: 'count', - name: i18n.translate('xpack.siem.endpoint.resolver.panel.table.row.count', { + name: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.table.row.count', { defaultMessage: 'Count', }), width: '20%', @@ -105,7 +105,7 @@ export const EventCountsForProcess = memo(function EventCountsForProcess({ }, { field: 'name', - name: i18n.translate('xpack.siem.endpoint.resolver.panel.table.row.eventType', { + name: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.table.row.eventType', { defaultMessage: 'Event Type', }), width: '80%', 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/panel_content_related_detail.tsx index 94924079d7958..2e59c9c625564 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/panel_content_related_detail.tsx @@ -113,14 +113,17 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ const processEntityId = parentEvent && event.entityId(parentEvent); const totalCount = countForParent || 0; const eventsString = i18n.translate( - 'xpack.siem.endpoint.resolver.panel.relatedEventDetail.events', + 'xpack.securitySolution.enpoint.resolver.panel.relatedEventDetail.events', { defaultMessage: 'Events', } ); - const naString = i18n.translate('xpack.siem.endpoint.resolver.panel.relatedEventDetail.NA', { - defaultMessage: 'N/A', - }); + const naString = i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.relatedEventDetail.NA', + { + defaultMessage: 'N/A', + } + ); const relatedsReadyMap = useSelector(selectors.relatedEventsReady); const relatedsReady = relatedsReadyMap.get(processEntityId!); @@ -264,9 +267,12 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ * If the ship hasn't come in yet, wait on the dock */ if (!relatedsReady) { - const waitingString = i18n.translate('xpack.siem.endpoint.resolver.panel.relatedDetail.wait', { - defaultMessage: 'Waiting For Events...', - }); + const waitingString = i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.relatedDetail.wait', + { + defaultMessage: 'Waiting For Events...', + } + ); return ( <> @@ -282,9 +288,12 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ * Could happen if user e.g. loads a URL with a bad crumbEvent */ if (!relatedEventToShowDetailsFor) { - const errString = i18n.translate('xpack.siem.endpoint.resolver.panel.relatedDetail.missing', { - defaultMessage: 'Related event not found.', - }); + const errString = i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.relatedDetail.missing', + { + defaultMessage: 'Related event not found.', + } + ); return ; } diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index b6e31c4539e84..c7bfc5a52a87d 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -89,13 +89,13 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr 0 ); const eventsString = i18n.translate( - 'xpack.siem.endpoint.resolver.panel.processEventListByType.events', + 'xpack.securitySolution.enpoint.resolver.panel.processEventListByType.events', { defaultMessage: 'Events', } ); const waitingString = i18n.translate( - 'xpack.siem.endpoint.resolver.panel.processEventListByType.wait', + 'xpack.securitySolution.enpoint.resolver.panel.processEventListByType.wait', { defaultMessage: 'Waiting For Events...', } diff --git a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx index bdc76f0b62902..7b9ddecdca72a 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx @@ -33,118 +33,139 @@ import { CrumbInfo } from './panels/panel_content_utilities'; * A map of all known event types (in ugly schema format) to beautifully i18n'd display names */ export const displayNameRecord = { - application: i18n.translate('xpack.siem.endpoint.resolver.applicationEventTypeDisplayName', { - defaultMessage: 'Application', - }), - apm: i18n.translate('xpack.siem.endpoint.resolver.apmEventTypeDisplayName', { + application: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.applicationEventTypeDisplayName', + { + defaultMessage: 'Application', + } + ), + apm: i18n.translate('xpack.securitySolution.enpoint.resolver.apmEventTypeDisplayName', { defaultMessage: 'APM', }), - audit: i18n.translate('xpack.siem.endpoint.resolver.auditEventTypeDisplayName', { + audit: i18n.translate('xpack.securitySolution.enpoint.resolver.auditEventTypeDisplayName', { defaultMessage: 'Audit', }), authentication: i18n.translate( - 'xpack.siem.endpoint.resolver.authenticationEventTypeDisplayName', + 'xpack.securitySolution.enpoint.resolver.authenticationEventTypeDisplayName', { defaultMessage: 'Authentication', } ), - certificate: i18n.translate('xpack.siem.endpoint.resolver.certificateEventTypeDisplayName', { - defaultMessage: 'Certificate', - }), - cloud: i18n.translate('xpack.siem.endpoint.resolver.cloudEventTypeDisplayName', { + certificate: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.certificateEventTypeDisplayName', + { + defaultMessage: 'Certificate', + } + ), + cloud: i18n.translate('xpack.securitySolution.enpoint.resolver.cloudEventTypeDisplayName', { defaultMessage: 'Cloud', }), - database: i18n.translate('xpack.siem.endpoint.resolver.databaseEventTypeDisplayName', { + database: i18n.translate('xpack.securitySolution.enpoint.resolver.databaseEventTypeDisplayName', { defaultMessage: 'Database', }), - driver: i18n.translate('xpack.siem.endpoint.resolver.driverEventTypeDisplayName', { + driver: i18n.translate('xpack.securitySolution.enpoint.resolver.driverEventTypeDisplayName', { defaultMessage: 'Driver', }), - email: i18n.translate('xpack.siem.endpoint.resolver.emailEventTypeDisplayName', { + email: i18n.translate('xpack.securitySolution.enpoint.resolver.emailEventTypeDisplayName', { defaultMessage: 'Email', }), - file: i18n.translate('xpack.siem.endpoint.resolver.fileEventTypeDisplayName', { + file: i18n.translate('xpack.securitySolution.enpoint.resolver.fileEventTypeDisplayName', { defaultMessage: 'File', }), - host: i18n.translate('xpack.siem.endpoint.resolver.hostEventTypeDisplayName', { + host: i18n.translate('xpack.securitySolution.enpoint.resolver.hostEventTypeDisplayName', { defaultMessage: 'Host', }), - iam: i18n.translate('xpack.siem.endpoint.resolver.iamEventTypeDisplayName', { + iam: i18n.translate('xpack.securitySolution.enpoint.resolver.iamEventTypeDisplayName', { defaultMessage: 'IAM', }), - iam_group: i18n.translate('xpack.siem.endpoint.resolver.iam_groupEventTypeDisplayName', { - defaultMessage: 'IAM Group', - }), + iam_group: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.iam_groupEventTypeDisplayName', + { + defaultMessage: 'IAM Group', + } + ), intrusion_detection: i18n.translate( - 'xpack.siem.endpoint.resolver.intrusion_detectionEventTypeDisplayName', + 'xpack.securitySolution.enpoint.resolver.intrusion_detectionEventTypeDisplayName', { defaultMessage: 'Intrusion Detection', } ), - malware: i18n.translate('xpack.siem.endpoint.resolver.malwareEventTypeDisplayName', { + malware: i18n.translate('xpack.securitySolution.enpoint.resolver.malwareEventTypeDisplayName', { defaultMessage: 'Malware', }), - network_flow: i18n.translate('xpack.siem.endpoint.resolver.network_flowEventTypeDisplayName', { - defaultMessage: 'Network Flow', - }), - network: i18n.translate('xpack.siem.endpoint.resolver.networkEventTypeDisplayName', { + network_flow: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.network_flowEventTypeDisplayName', + { + defaultMessage: 'Network Flow', + } + ), + network: i18n.translate('xpack.securitySolution.enpoint.resolver.networkEventTypeDisplayName', { defaultMessage: 'Network', }), - package: i18n.translate('xpack.siem.endpoint.resolver.packageEventTypeDisplayName', { + package: i18n.translate('xpack.securitySolution.enpoint.resolver.packageEventTypeDisplayName', { defaultMessage: 'Package', }), - process: i18n.translate('xpack.siem.endpoint.resolver.processEventTypeDisplayName', { + process: i18n.translate('xpack.securitySolution.enpoint.resolver.processEventTypeDisplayName', { defaultMessage: 'Process', }), - registry: i18n.translate('xpack.siem.endpoint.resolver.registryEventTypeDisplayName', { + registry: i18n.translate('xpack.securitySolution.enpoint.resolver.registryEventTypeDisplayName', { defaultMessage: 'Registry', }), - session: i18n.translate('xpack.siem.endpoint.resolver.sessionEventTypeDisplayName', { + session: i18n.translate('xpack.securitySolution.enpoint.resolver.sessionEventTypeDisplayName', { defaultMessage: 'Session', }), - service: i18n.translate('xpack.siem.endpoint.resolver.serviceEventTypeDisplayName', { + service: i18n.translate('xpack.securitySolution.enpoint.resolver.serviceEventTypeDisplayName', { defaultMessage: 'Service', }), - socket: i18n.translate('xpack.siem.endpoint.resolver.socketEventTypeDisplayName', { + socket: i18n.translate('xpack.securitySolution.enpoint.resolver.socketEventTypeDisplayName', { defaultMessage: 'Socket', }), - vulnerability: i18n.translate('xpack.siem.endpoint.resolver.vulnerabilityEventTypeDisplayName', { - defaultMessage: 'Vulnerability', - }), - web: i18n.translate('xpack.siem.endpoint.resolver.webEventTypeDisplayName', { + vulnerability: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.vulnerabilityEventTypeDisplayName', + { + defaultMessage: 'Vulnerability', + } + ), + web: i18n.translate('xpack.securitySolution.enpoint.resolver.webEventTypeDisplayName', { defaultMessage: 'Web', }), - alert: i18n.translate('xpack.siem.endpoint.resolver.alertEventTypeDisplayName', { + alert: i18n.translate('xpack.securitySolution.enpoint.resolver.alertEventTypeDisplayName', { defaultMessage: 'Alert', }), - security: i18n.translate('xpack.siem.endpoint.resolver.securityEventTypeDisplayName', { + security: i18n.translate('xpack.securitySolution.enpoint.resolver.securityEventTypeDisplayName', { defaultMessage: 'Security', }), - dns: i18n.translate('xpack.siem.endpoint.resolver.dnsEventTypeDisplayName', { + dns: i18n.translate('xpack.securitySolution.enpoint.resolver.dnsEventTypeDisplayName', { defaultMessage: 'DNS', }), - clr: i18n.translate('xpack.siem.endpoint.resolver.clrEventTypeDisplayName', { + clr: i18n.translate('xpack.securitySolution.enpoint.resolver.clrEventTypeDisplayName', { defaultMessage: 'CLR', }), - image_load: i18n.translate('xpack.siem.endpoint.resolver.image_loadEventTypeDisplayName', { - defaultMessage: 'Image Load', - }), - powershell: i18n.translate('xpack.siem.endpoint.resolver.powershellEventTypeDisplayName', { - defaultMessage: 'Powershell', - }), - wmi: i18n.translate('xpack.siem.endpoint.resolver.wmiEventTypeDisplayName', { + image_load: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.image_loadEventTypeDisplayName', + { + defaultMessage: 'Image Load', + } + ), + powershell: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.powershellEventTypeDisplayName', + { + defaultMessage: 'Powershell', + } + ), + wmi: i18n.translate('xpack.securitySolution.enpoint.resolver.wmiEventTypeDisplayName', { defaultMessage: 'WMI', }), - api: i18n.translate('xpack.siem.endpoint.resolver.apiEventTypeDisplayName', { + api: i18n.translate('xpack.securitySolution.enpoint.resolver.apiEventTypeDisplayName', { defaultMessage: 'API', }), - user: i18n.translate('xpack.siem.endpoint.resolver.userEventTypeDisplayName', { + user: i18n.translate('xpack.securitySolution.enpoint.resolver.userEventTypeDisplayName', { defaultMessage: 'User', }), } as const; const unknownEventTypeMessage = i18n.translate( - 'xpack.siem.endpoint.resolver.userEventTypeDisplayUnknown', + 'xpack.securitySolution.enpoint.resolver.userEventTypeDisplayUnknown', { defaultMessage: 'Unknown', } From 877d168c5947bb719ae9461c4cc75345e9fabe7f Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Sun, 14 Jun 2020 00:40:00 -0400 Subject: [PATCH 22/78] React key warnings --- .../panels/panel_content_related_detail.tsx | 6 +- .../panels/panel_content_related_list.tsx | 6 +- .../view/panels/panel_content_utilities.tsx | 10 +- .../resolver/view/process_event_dot.tsx | 269 +++++++++--------- .../public/resolver/view/submenu.tsx | 4 +- 5 files changed, 142 insertions(+), 153 deletions(-) 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/panel_content_related_detail.tsx index 2e59c9c625564..97b3109d011ca 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/panel_content_related_detail.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { memo, useMemo, useEffect } from 'react'; +import React, { memo, useMemo, useEffect, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiI18nNumber, @@ -313,7 +313,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ {sections.map(({ sectionTitle, entries }, index) => { return ( - <> + {index === 0 ? null : } @@ -331,7 +331,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ listItems={entries} /> {index === sections.length - 1 ? null : } - + ); })} diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index c7bfc5a52a87d..3c23b3677ee15 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { memo, useMemo, useEffect } from 'react'; +import React, { memo, useMemo, useEffect, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiTitle, @@ -54,7 +54,7 @@ const DisplayList = memo(function DisplayList({ <> {matchingEventEntries.map((eventView, index) => { return ( - <> + {eventView.eventType} {' @ '} @@ -63,7 +63,7 @@ const DisplayList = memo(function DisplayList({ {eventView.name} {index === matchingEventEntries.length - 1 ? null : } - + ); })} diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx index 93cff31d7c56c..3a6560475f917 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx @@ -18,10 +18,10 @@ export interface CrumbInfo { readonly crumbEvent: string; } -const ThemedBreadcrumbs = styled(EuiBreadcrumbs)<{ crumbsBackground: string; crumbText: string }>` +const ThemedBreadcrumbs = styled(EuiBreadcrumbs)<{ background: string; text: string }>` &.euiBreadcrumbs.euiBreadcrumbs--responsive { - background-color: ${(props) => props.crumbsBackground}; - color: ${(props) => props.crumbText}; + background-color: ${(props) => props.background}; + color: ${(props) => props.text}; padding: 1em; } `; @@ -41,8 +41,8 @@ export const StyledBreadcrumbs = memo(function StyledBreadcrumbs({ } = useResolverTheme(); return ( diff --git a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx index 7b9ddecdca72a..c5002cb71688e 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx @@ -7,14 +7,7 @@ import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; -import { - htmlIdGenerator, - EuiButton, - EuiI18nNumber, - EuiKeyboardAccessible, - EuiFlexGroup, - EuiFlexItem, -} from '@elastic/eui'; +import { htmlIdGenerator, EuiButton, EuiI18nNumber, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; // eslint-disable-next-line import/no-nodejs-modules @@ -504,148 +497,144 @@ const ProcessEventDotComponents = React.memo( * Key event handling (e.g. 'Enter'/'Space') is provisioned by the `EuiKeyboardAccessible` component */ return ( - -
+ - + + + + + + + + + {descriptionText} + +
= 2 ? 'euiButton' : 'euiButton euiButton--small'} + data-test-subject="nodeLabel" + id={labelId} + onClick={handleClick} + onFocus={handleFocus} + tabIndex={-1} style={{ - display: 'block', - width: '100%', - height: '100%', - position: 'absolute', - top: '0', - left: '0', + backgroundColor: colorMap.resolverBackground, + alignSelf: 'flex-start', + padding: 0, }} > - - - - - - - - - - {descriptionText} - -
= 2 ? 'euiButton' : 'euiButton euiButton--small'} + - - - - {eventModel.eventName(event)} - + + + {eventModel.eventName(event)} - -
- - - - - - - - -
-
- + + +
+ + + + + + + + + + ); /* eslint-enable jsx-a11y/click-events-have-key-events */ } diff --git a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx index 0dbb6d22ca3b7..8f972dd737af6 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx @@ -105,10 +105,10 @@ const OptionList = React.memo( actionToTake(); } } - setOptions(options); + setOptions(newOptions); }, }; - }, [actionsByLabel, options]); + }, [actionsByLabel]); return ( Date: Mon, 15 Jun 2020 10:51:05 -0400 Subject: [PATCH 23/78] fixing names --- .../common/endpoint/models/event.ts | 13 ++++++++----- .../view/panels/panel_content_related_detail.tsx | 4 ++-- .../view/panels/panel_content_related_list.tsx | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index 01a2153c8d099..26694a24ec91f 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -53,7 +53,10 @@ export function parentEntityId(event: ResolverEvent): string | undefined { return event.process.parent?.entity_id; } -export function eventType(event: ResolverEvent): string { +/** + * @param event The event to get the category for + */ +export function eventCategory(event: ResolverEvent): string { // Returning "Process" as a catch-all here because it seems pretty general let eventCategoryToReturn: string = 'Process'; if (isLegacyEvent(event)) { @@ -63,11 +66,11 @@ export function eventType(event: ResolverEvent): string { } } else { const eventCategories = event.event.category; - const eventCategory = + const category = typeof eventCategories === 'string' ? eventCategories : eventCategories[0] || ''; - if (eventCategory) { - eventCategoryToReturn = eventCategory; + if (category) { + eventCategoryToReturn = category; } } return eventCategoryToReturn; @@ -133,7 +136,7 @@ export function descriptiveName(event: ResolverEvent): string { return eventName(event); } // For the purposes of providing a descriptive name, we're taking the first entry in the `event.type` - const ecsCategory = eventType(event); + const ecsCategory = eventCategory(event); /** * This list of attempts can be expanded/adjusted as the underlying model changes over time: 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/panel_content_related_detail.tsx index 97b3109d011ca..317a4ce445065 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/panel_content_related_detail.tsx @@ -154,9 +154,9 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ (evt) => event.eventId(evt) === relatedEventId ); // For breadcrumbs: - const specificCategory = specificEvent && event.eventType(specificEvent); + const specificCategory = specificEvent && event.eventCategory(specificEvent); const countOfCategory = relatedEventsForThisProcess.events.reduce((sumtotal, evt) => { - return event.eventType(evt) === specificCategory ? sumtotal + 1 : sumtotal; + return event.eventCategory(evt) === specificCategory ? sumtotal + 1 : sumtotal; }, 0); return [specificEvent, countOfCategory, specificCategory || naString]; }, [relatedEventsForThisProcess, naString, relatedEventId]); diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index 3c23b3677ee15..1ea8675b49e23 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -139,7 +139,7 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr const matchingEventEntries: MatchingEventEntry[] = useMemo(() => { const relateds = relatedEventsToDisplay .reduce((a: ResolverEvent[], candidate) => { - if (event.eventType(candidate) === eventType) { + if (event.eventCategory(candidate) === eventType) { a.push(candidate); } return a; From 0ee1886cbbae51eff9db68908d61063407f4c106 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 11:12:09 -0400 Subject: [PATCH 24/78] J. Buttner review: name changes for process --- .../public/resolver/models/process_event.ts | 6 +++--- .../view/panels/panel_content_process_detail.tsx | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts index 9938c5766d607..9f17b9f741178 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts @@ -81,7 +81,7 @@ export function uniquePidForProcess(passedEvent: ResolverEvent): string { /** * Returns the pid for the process on the host */ -export function hostPidForProcess(passedEvent: ResolverEvent): string { +export function processPid(passedEvent: ResolverEvent): string { if (event.isLegacyEvent(passedEvent)) { return String(passedEvent.endgame.unique_pid); } else { @@ -103,7 +103,7 @@ export function uniqueParentPidForProcess(passedEvent: ResolverEvent): string | /** * Returns the process event's parent pid */ -export function hostParentPidForProcess(passedEvent: ResolverEvent): string | undefined { +export function processParentPid(passedEvent: ResolverEvent): string | undefined { if (event.isLegacyEvent(passedEvent)) { return String(passedEvent.endgame.unique_ppid); } else { @@ -115,7 +115,7 @@ export function hostParentPidForProcess(passedEvent: ResolverEvent): string | un /** * Returns the process event's path on its host */ -export function hostPathForProcess(passedEvent: ResolverEvent): string | undefined { +export function processPath(passedEvent: ResolverEvent): string | undefined { if (event.isLegacyEvent(passedEvent)) { return passedEvent.endgame.process_path; } else { 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 e9064ff305629..cdcf815429e56 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 @@ -17,10 +17,10 @@ import styled from 'styled-components'; import * as event from '../../../../common/endpoint/models/event'; import { CrumbInfo, formatDate, StyledBreadcrumbs } from './panel_content_utilities'; import { - hostPathForProcess, - hostPidForProcess, + processPath, + processPid, userInfoForProcess, - hostParentPidForProcess, + processParentPid, md5HashForProcess, argsForProcess, } from '../../models/process_event'; @@ -60,10 +60,10 @@ export const ProcessDetails = memo(function ProcessDetails({ })]: dateTime, [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.path', { defaultMessage: 'Path', - })]: hostPathForProcess(processEvent), + })]: processPath(processEvent), [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.pid', { defaultMessage: 'PID', - })]: hostPidForProcess(processEvent), + })]: processPid(processEvent), [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.user', { defaultMessage: 'User', })]: (userInfoForProcess(processEvent) as { name: string; domain: string }).name, @@ -74,7 +74,7 @@ export const ProcessDetails = memo(function ProcessDetails({ { defaultMessage: 'Parent PID', } - )]: hostParentPidForProcess(processEvent), + )]: processParentPid(processEvent), })]: (userInfoForProcess(processEvent) as { name: string; domain: string }).domain, [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.md5hash', { defaultMessage: 'MD5', From 499d9429daad35c88fea0e9945f2dd780e910c58 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 11:15:30 -0400 Subject: [PATCH 25/78] J Buttner review: annotate return type --- .../security_solution/public/resolver/models/process_event.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts index 9f17b9f741178..f1f291e10baa7 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts @@ -126,7 +126,7 @@ export function processPath(passedEvent: ResolverEvent): string | undefined { /** * Returns the username for the account that ran the process */ -export function userInfoForProcess(passedEvent: ResolverEvent): object | undefined { +export function userInfoForProcess(passedEvent: ResolverEvent): {user?: string, domain?: string} | undefined { return passedEvent.user; } From 03da2ead0dc0f06b58c3b2f66b2a1a6a4e966669 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 11:51:04 -0400 Subject: [PATCH 26/78] lint --- .../security_solution/public/resolver/models/process_event.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts index f1f291e10baa7..65b6ce7d0894a 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts @@ -126,7 +126,9 @@ export function processPath(passedEvent: ResolverEvent): string | undefined { /** * Returns the username for the account that ran the process */ -export function userInfoForProcess(passedEvent: ResolverEvent): {user?: string, domain?: string} | undefined { +export function userInfoForProcess( + passedEvent: ResolverEvent +): { user?: string; domain?: string } | undefined { return passedEvent.user; } From 90a9fc176ff86799e50706668bda2c808ce9371c Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 12:38:23 -0400 Subject: [PATCH 27/78] M Olorunnisola review: change prop name to be more clear --- .../public/resolver/view/panels/panel_content_error.tsx | 8 ++++---- .../resolver/view/panels/panel_content_related_detail.tsx | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx index 86c63e87bd799..df047cd82aa19 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx @@ -13,13 +13,13 @@ import { CrumbInfo, StyledBreadcrumbs } from './panel_content_utilities'; * Display an error in the panel when something goes wrong and give the user a way to "retreat" back to a default state. * * @param {function} pushToQueryparams A function to update the hash value in the URL to control panel state - * @param {string} errorMessage The message to display in the panel when something goes wrong + * @param {string} translatedErrorMessage The message to display in the panel when something goes wrong */ export const PanelContentError = memo(function ({ - errorMessage, + translatedErrorMessage, pushToQueryParams, }: { - errorMessage: string; + translatedErrorMessage: string; pushToQueryParams: (arg0: CrumbInfo) => unknown; }) { const crumbs = useMemo(() => { @@ -44,7 +44,7 @@ export const PanelContentError = memo(function ({ <> - {errorMessage} + {translatedErrorMessage} { 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/panel_content_related_detail.tsx index 317a4ce445065..eb796fd90f4f8 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/panel_content_related_detail.tsx @@ -294,7 +294,9 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ defaultMessage: 'Related event not found.', } ); - return ; + return ( + + ); } return ( From 58241005856499c357290c37fcee9a6c5585e75d Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 14:41:29 -0400 Subject: [PATCH 28/78] M Olorunnisola review: fix idFromParams to be more radable --- .../security_solution/public/resolver/view/panel.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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 899691b892630..c4c54c4c60566 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -54,10 +54,13 @@ const PanelContent = memo(function PanelContent() { // The entity id in query params of a graphable process (or false if none is found) // For 1 case (the related detail, see below), the process id will be in crumbEvent instead of crumbId const idFromParams = useMemo(() => { - return ( - (graphableProcessEntityIds.has(queryParams.crumbId) && queryParams.crumbId) || - (graphableProcessEntityIds.has(queryParams.crumbEvent) && queryParams.crumbEvent) - ); + if (graphableProcessEntityIds.has(queryParams.crumbId)) { + return queryParams.crumbId; + } + if (graphableProcessEntityIds.has(queryParams.crumbEvent)) { + return queryParams.crumbEvent; + } + return ''; }, [queryParams, graphableProcessEntityIds]); // The "selected" node (and its corresponding event) in the tree control. From be839a21ab45d854f09789b8eb18124ef57687fd Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 15:51:19 -0400 Subject: [PATCH 29/78] M Olorunnisola review: more reable descriptiveName --- .../common/endpoint/models/event.ts | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index 26694a24ec91f..47f36b9fe27b6 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -135,37 +135,41 @@ export function descriptiveName(event: ResolverEvent): string { if (isLegacyEvent(event)) { return eventName(event); } - // For the purposes of providing a descriptive name, we're taking the first entry in the `event.type` - const ecsCategory = eventCategory(event); /** * This list of attempts can be expanded/adjusted as the underlying model changes over time: */ // Stable, per ECS 1.5: https://www.elastic.co/guide/en/ecs/current/ecs-allowed-values-event-category.html - if (ecsCategory === 'network' && isRecordNamable(event, ecsCategory)) { - if (event?.network?.forwarded_ip) { - return `${event?.network?.direction ? `${event?.network?.direction} ` : ''}${ - event.network.forwarded_ip - }`; + const namableNetworkEvent = isRecordNamable(event, 'network') && event; + const namableFileEvent = isRecordNamable(event, 'file') && event; + const namableRegistryEvent = isRecordNamable(event, 'registry') && event; + const namableDNSEvent = isRecordNamable(event, 'dns') && event; + + if (namableNetworkEvent) { + if (namableNetworkEvent.network.forwarded_ip) { + return `${ + namableNetworkEvent.network.direction ? `${namableNetworkEvent.network.direction} ` : '' + }${namableNetworkEvent.network.forwarded_ip}`; } } - if (ecsCategory === 'file' && isRecordNamable(event, ecsCategory)) { - if (event?.file?.path) { - return `${event.file.path}`; + + if (namableFileEvent) { + if (namableFileEvent.file.path) { + return `${namableFileEvent.file.path}`; } } // Extended categories (per ECS 1.5): - if (ecsCategory === 'registry' && isRecordNamable(event, ecsCategory)) { - const pathOrKey = event?.registry?.path || event?.registry?.key; + if (namableRegistryEvent) { + const pathOrKey = namableRegistryEvent.registry.path || namableRegistryEvent.registry.key; if (pathOrKey) { return `${pathOrKey}`; } } - if (ecsCategory === 'dns' && isRecordNamable(event, ecsCategory)) { - if (event?.dns?.question?.name) { - return `${event.dns.question.name}`; + if (namableDNSEvent) { + if (namableDNSEvent.dns.question.name) { + return `${namableDNSEvent.dns.question.name}`; } } From 96732aef84aeb5b5d3320680bf312abbbbb44e21 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 16:01:56 -0400 Subject: [PATCH 30/78] M Olorunnisola review: parameter readability in middleware --- .../public/endpoint_alerts/store/middleware.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts b/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts index 0d6cf718a62cd..571175bc551b8 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts @@ -31,10 +31,11 @@ let lastSelectedAlert: string | null = null; */ const selectedAlertHasChanged = (params: ReturnType): boolean => { const { selected_alert: selectedAlert } = params; - if (typeof selectedAlert !== 'string') { + const shouldNotChange = selectedAlert === lastSelectedAlert; + if (shouldNotChange) { return false; } - if (selectedAlert === lastSelectedAlert) { + if (typeof selectedAlert !== 'string') { return false; } lastSelectedAlert = selectedAlert; From 0548b071933b106d4b259e912218f58372c6fcd1 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 16:05:46 -0400 Subject: [PATCH 31/78] J Buttner review: set domain as optional type --- x-pack/plugins/security_solution/common/endpoint/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/types.ts b/x-pack/plugins/security_solution/common/endpoint/types.ts index 90e2f841f4ade..9ea2ac9f16325 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types.ts @@ -450,7 +450,7 @@ export interface EndpointEvent { }; }; user: { - domain: string; + domain?: string; name: string; }; } From bef40b6fe821843753f0cfc33311f971895ceb76 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 16:08:47 -0400 Subject: [PATCH 32/78] R Austin review: does not comport --- x-pack/plugins/security_solution/public/resolver/view/panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c4c54c4c60566..ee271f1edcf3f 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -24,7 +24,7 @@ import { RelatedEventDetail } from './panels/panel_content_related_detail'; import { CrumbInfo } from './panels/panel_content_utilities'; /** - * The team decided to use this determinant to express how we comport state in the UI with the values of the two query params: + * The team decided to use this table to determine which breadcrumbs/view to display: * * | Crumb/Table | &crumbId | &crumbEvent | * | :--------------------- | :------------------------- | :---------------------- | From 6772323a531fb525a5d372225a557d999794ce5f Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 16:15:47 -0400 Subject: [PATCH 33/78] J Buttner review: remove unnecessary pid generation --- .../plugins/security_solution/common/endpoint/generate_data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index 6e698f240db1a..6d8df4a4228ab 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -677,7 +677,7 @@ export class EndpointDocGenerator { ): Event[] { const events = []; const startDate = new Date().getTime(); - const root = this.generateEvent({ timestamp: startDate + 1000, pid: this.randomN(5000) }); + const root = this.generateEvent({ timestamp: startDate + 1000 }); events.push(root); let ancestor = root; let timestamp = root['@timestamp'] + 1000; From ee1f0e69e885e625bc62179d059a58d6116334f1 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 18:42:49 -0400 Subject: [PATCH 34/78] R Austin review: Use broader i18n input --- .../panels/panel_content_related_counts.tsx | 16 +++---- .../panels/panel_content_related_detail.tsx | 36 +++++++++++---- .../panels/panel_content_related_list.tsx | 46 ++++++++++++------- 3 files changed, 63 insertions(+), 35 deletions(-) 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/panel_content_related_counts.tsx index 409f849529517..218014c885227 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/panel_content_related_counts.tsx @@ -6,13 +6,8 @@ import React, { memo, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import { - EuiI18nNumber, - EuiBasicTableColumn, - EuiButtonEmpty, - EuiSpacer, - EuiInMemoryTable, -} from '@elastic/eui'; +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'; @@ -73,8 +68,11 @@ export const EventCountsForProcess = memo(function EventCountsForProcess({ { text: ( <> - - {/* Non-breaking space->*/ ` ${eventsString}`} + ), onClick: () => { 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/panel_content_related_detail.tsx index eb796fd90f4f8..07ec43ff017df 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/panel_content_related_detail.tsx @@ -7,7 +7,6 @@ import React, { memo, useMemo, useEffect, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { - EuiI18nNumber, EuiSpacer, EuiText, EuiDescriptionList, @@ -17,6 +16,7 @@ import { } from '@elastic/eui'; import styled from 'styled-components'; import { useSelector } from 'react-redux'; +import { FormattedMessage } from 'react-intl'; import { CrumbInfo, formatDate, StyledBreadcrumbs } from './panel_content_utilities'; import * as event from '../../../../common/endpoint/models/event'; import { ResolverEvent } from '../../../../common/endpoint/types'; @@ -222,8 +222,11 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ { text: ( <> - - {/* Non-breaking space->*/ ` ${eventsString}`} + ), onClick: () => { @@ -233,8 +236,11 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ { text: ( <> - - {/* Non-breaking space->*/ ` ${relatedEventCategory}`} + ), onClick: () => { @@ -304,11 +310,21 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ - {`${relatedEventCategory} ${event.ecsEventType( - relatedEventToShowDetailsFor - )}`} - {' @ '} - {formattedDate} + + + + {event.descriptiveName(relatedEventToShowDetailsFor)} diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index 1ea8675b49e23..d2b427a5bfe68 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -6,15 +6,9 @@ import React, { memo, useMemo, useEffect, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; -import { - EuiTitle, - EuiI18nNumber, - EuiSpacer, - EuiText, - EuiButtonEmpty, - EuiHorizontalRule, -} from '@elastic/eui'; +import { EuiTitle, EuiSpacer, EuiText, EuiButtonEmpty, EuiHorizontalRule } from '@elastic/eui'; import { useSelector } from 'react-redux'; +import { FormattedMessage } from 'react-intl'; import { CrumbInfo, formatDate, StyledBreadcrumbs } from './panel_content_utilities'; import * as event from '../../../../common/endpoint/models/event'; import { BoldCode } from './panel_content_related_detail'; @@ -35,6 +29,7 @@ import { useResolverDispatch } from '../use_resolver_dispatch'; interface MatchingEventEntry { formattedDate: string; eventType: string; + eventCategory: string; name: string; entityId: string; setQueryParams: () => void; @@ -56,9 +51,21 @@ const DisplayList = memo(function DisplayList({ return ( - {eventView.eventType} - {' @ '} - {eventView.formattedDate} + + + + {eventView.name} @@ -150,7 +157,8 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr const entityId = event.eventId(resolverEvent); return { formattedDate, - eventType: `${eventType} ${event.ecsEventType(resolverEvent)}`, + eventCategory: `${eventType}`, + eventType: `${event.ecsEventType(resolverEvent)}`, name: event.descriptiveName(resolverEvent), entityId, setQueryParams: () => { @@ -178,8 +186,11 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr { text: ( <> - - {/* Non-breaking space->*/ ` ${eventsString}`} + ), onClick: () => { @@ -189,8 +200,11 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr { text: ( <> - - {/* Non-breaking space->*/ ` ${eventType}`} + ), onClick: () => {}, From 7c7fd1fc22ffd25cdbac9b8072dc16afe46408d2 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 18:45:20 -0400 Subject: [PATCH 35/78] R Austin review: i18n on list comp. --- .../panels/panel_content_related_list.tsx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index d2b427a5bfe68..77a2ee0b6f470 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -51,21 +51,21 @@ const DisplayList = memo(function DisplayList({ return ( - + + + - - {eventView.name} From 48347d1f7cabb7a0738620bb384fdbf42961c34a Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 15 Jun 2020 22:45:29 -0400 Subject: [PATCH 36/78] R Austin review: remove stray line --- .../security_solution/public/resolver/store/data/reducer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts b/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts index 5d57486012892..d7db4ed9bb20a 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts @@ -39,7 +39,6 @@ export const dataReducer: Reducer = (state = initialS hasError: true, }; } else if (action.type === 'userRequestedRelatedEventData') { - state.relatedEventsReady.set(action.payload, false); return { ...state, relatedEventsReady: new Map(state.relatedEventsReady.set(action.payload, false)), From 54a451f53b2f70f0ad7578e300a872404b149678 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Tue, 16 Jun 2020 10:45:52 -0400 Subject: [PATCH 37/78] R Austin review: move check inside middleware --- .../endpoint_alerts/store/middleware.ts | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts b/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts index be6829fc19bb0..ae5d36ce0d1ff 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/store/middleware.ts @@ -22,30 +22,30 @@ import { isAlertPageTabChange, } from './selectors'; -let lastSelectedAlert: string | null = null; -/** - * @returns true once per change of `selectedAlert` in query params. - * - * As opposed to `hasSelectedAlert` which always returns true if the alert is present - * query params, which can cause unnecessary requests and re-renders in some cases. - */ -const selectedAlertHasChanged = (params: ReturnType): boolean => { - const { selected_alert: selectedAlert } = params; - const shouldNotChange = selectedAlert === lastSelectedAlert; - if (shouldNotChange) { - return false; - } - if (typeof selectedAlert !== 'string') { - return false; - } - lastSelectedAlert = selectedAlert; - return true; -}; - export const alertMiddlewareFactory: ImmutableMiddlewareFactory = ( coreStart, depsStart ) => { + let lastSelectedAlert: string | null = null; + /** + * @returns true once per change of `selectedAlert` in query params. + * + * As opposed to `hasSelectedAlert` which always returns true if the alert is present + * query params, which can cause unnecessary requests and re-renders in some cases. + */ + const selectedAlertHasChanged = (params: ReturnType): boolean => { + const { selected_alert: selectedAlert } = params; + const shouldNotChange = selectedAlert === lastSelectedAlert; + if (shouldNotChange) { + return false; + } + if (typeof selectedAlert !== 'string') { + return false; + } + lastSelectedAlert = selectedAlert; + return true; + }; + async function fetchIndexPatterns(): Promise { const { indexPatterns } = depsStart.data; const fields = await indexPatterns.getFieldsForWildcard({ From 8b4a3b344e0e67164479668e1dad0230327b27de Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Tue, 16 Jun 2020 13:28:51 -0400 Subject: [PATCH 38/78] R Austin review: use String() instead --- .../security_solution/common/endpoint/models/event.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index 47f36b9fe27b6..484a4d9da54f4 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -156,7 +156,7 @@ export function descriptiveName(event: ResolverEvent): string { if (namableFileEvent) { if (namableFileEvent.file.path) { - return `${namableFileEvent.file.path}`; + return String(namableFileEvent.file.path); } } @@ -164,12 +164,12 @@ export function descriptiveName(event: ResolverEvent): string { if (namableRegistryEvent) { const pathOrKey = namableRegistryEvent.registry.path || namableRegistryEvent.registry.key; if (pathOrKey) { - return `${pathOrKey}`; + return String(pathOrKey); } } if (namableDNSEvent) { if (namableDNSEvent.dns.question.name) { - return `${namableDNSEvent.dns.question.name}`; + return String(namableDNSEvent.dns.question.name); } } From 951ef244c9eb225b30d36f5dc1c5bab99c632bb9 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Tue, 16 Jun 2020 13:47:09 -0400 Subject: [PATCH 39/78] R Austin review: return number instead --- .../public/resolver/models/process_event.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts index 65b6ce7d0894a..a827e682e29a9 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts @@ -103,12 +103,12 @@ export function uniqueParentPidForProcess(passedEvent: ResolverEvent): string | /** * Returns the process event's parent pid */ -export function processParentPid(passedEvent: ResolverEvent): string | undefined { +export function processParentPid(passedEvent: ResolverEvent): number | undefined { if (event.isLegacyEvent(passedEvent)) { - return String(passedEvent.endgame.unique_ppid); + return passedEvent.endgame.unique_ppid; } else { const ppid = passedEvent.process.parent?.pid; - return ppid ? String(ppid) : undefined; + return ppid ? ppid : undefined; } } From efb24f1604b5d81851776cb55f4fca514569d62d Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Tue, 16 Jun 2020 14:37:27 -0400 Subject: [PATCH 40/78] R Austin review: format compound message --- .../view/panels/panel_content_process_detail.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 cdcf815429e56..2b7c2f851b558 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 @@ -14,6 +14,7 @@ import { EuiDescriptionList, } from '@elastic/eui'; import styled from 'styled-components'; +import { FormattedMessage } from 'react-intl'; import * as event from '../../../../common/endpoint/models/event'; import { CrumbInfo, formatDate, StyledBreadcrumbs } from './panel_content_utilities'; import { @@ -111,10 +112,15 @@ export const ProcessDetails = memo(function ProcessDetails({ }, }, { - text: - i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.details', { - defaultMessage: 'Details for: ', - }) + processName, + text: ( + <> + + + ), onClick: () => {}, }, ]; From 532233a3be0f02cdd84a516d0c9235915f1f1240 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Tue, 16 Jun 2020 17:04:00 -0400 Subject: [PATCH 41/78] R Austin review: recomport for readability --- .../panels/panel_content_process_detail.tsx | 127 ++++++++++++------ 1 file changed, 89 insertions(+), 38 deletions(-) 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 2b7c2f851b558..b30c1c6c49cd0 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 @@ -54,48 +54,99 @@ export const ProcessDetails = memo(function ProcessDetails({ dateTime = formatDate(eventTime); } - const processInfo = processEvent - ? { - [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.created', { - defaultMessage: 'Created', - })]: dateTime, - [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.path', { - defaultMessage: 'Path', - })]: processPath(processEvent), - [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.pid', { - defaultMessage: 'PID', - })]: processPid(processEvent), - [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.user', { - defaultMessage: 'User', - })]: (userInfoForProcess(processEvent) as { name: string; domain: string }).name, - [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.domain', { - defaultMessage: 'Domain', - [i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.processDescList.parentPid', - { - defaultMessage: 'Parent PID', - } - )]: processParentPid(processEvent), - })]: (userInfoForProcess(processEvent) as { name: string; domain: string }).domain, - [i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.md5hash', { - defaultMessage: 'MD5', - })]: md5HashForProcess(processEvent), - [i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.processDescList.commandLine', - { - defaultMessage: 'Command Line', - } - )]: argsForProcess(processEvent), + const createdEntry = { + title: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.processDescList.created', + { + defaultMessage: 'Created', } - : {}; + ), + description: dateTime, + }; - return Object.entries(processInfo) - .filter(([, description]) => { - return description; + const pathEntry = { + title: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.path', { + defaultMessage: 'Path', + }), + description: processPath(processEvent), + }; + + const pidEntry = { + title: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.pid', { + defaultMessage: 'PID', + }), + description: processPid(processEvent), + }; + + const userEntry = { + title: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.user', { + defaultMessage: 'User', + }), + description: (userInfoForProcess(processEvent) as { name: string }).name, + }; + + const domainEntry = { + title: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.processDescList.domain', + { + defaultMessage: 'Domain', + } + ), + description: (userInfoForProcess(processEvent) as { domain: string }).domain, + }; + + const parentPidEntry = { + title: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.processDescList.parentPid', + { + defaultMessage: 'Parent PID', + } + ), + description: processParentPid(processEvent), + }; + + const md5Entry = { + title: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.processDescList.md5hash', + { + defaultMessage: 'MD5', + } + ), + description: md5HashForProcess(processEvent), + }; + + const commandLineEntry = { + title: i18n.translate( + 'xpack.securitySolution.enpoint.resolver.panel.processDescList.commandLine', + { + defaultMessage: 'Command Line', + } + ), + description: argsForProcess(processEvent), + }; + + // This is the data in {title, description} form for the EUIDescriptionList to display + const processDescriptionListData = [ + createdEntry, + pathEntry, + pidEntry, + userEntry, + domainEntry, + parentPidEntry, + md5Entry, + commandLineEntry, + ] + .filter((entry) => { + return entry.description; }) - .map(([title, description]) => { - return { title, description: String(description) }; + .map((entry) => { + return { + ...entry, + description: String(entry.description), + }; }); + + return processDescriptionListData; }, [processEvent]); const crumbs = useMemo(() => { From ef9b0280b4c4d1441316cc8b60a19d2473060e92 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 09:14:05 -0400 Subject: [PATCH 42/78] R Austin review: name to primaryEventCategory --- .../plugins/security_solution/common/endpoint/models/event.ts | 2 +- .../resolver/view/panels/panel_content_related_detail.tsx | 4 ++-- .../resolver/view/panels/panel_content_related_list.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index 484a4d9da54f4..46bddbec38b08 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -56,7 +56,7 @@ export function parentEntityId(event: ResolverEvent): string | undefined { /** * @param event The event to get the category for */ -export function eventCategory(event: ResolverEvent): string { +export function primaryEventCategory(event: ResolverEvent): string { // Returning "Process" as a catch-all here because it seems pretty general let eventCategoryToReturn: string = 'Process'; if (isLegacyEvent(event)) { 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/panel_content_related_detail.tsx index 07ec43ff017df..b447b5e062ded 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/panel_content_related_detail.tsx @@ -154,9 +154,9 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ (evt) => event.eventId(evt) === relatedEventId ); // For breadcrumbs: - const specificCategory = specificEvent && event.eventCategory(specificEvent); + const specificCategory = specificEvent && event.primaryEventCategory(specificEvent); const countOfCategory = relatedEventsForThisProcess.events.reduce((sumtotal, evt) => { - return event.eventCategory(evt) === specificCategory ? sumtotal + 1 : sumtotal; + return event.primaryEventCategory(evt) === specificCategory ? sumtotal + 1 : sumtotal; }, 0); return [specificEvent, countOfCategory, specificCategory || naString]; }, [relatedEventsForThisProcess, naString, relatedEventId]); diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index 77a2ee0b6f470..ea2ce27085efe 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -146,7 +146,7 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr const matchingEventEntries: MatchingEventEntry[] = useMemo(() => { const relateds = relatedEventsToDisplay .reduce((a: ResolverEvent[], candidate) => { - if (event.eventCategory(candidate) === eventType) { + if (event.primaryEventCategory(candidate) === eventType) { a.push(candidate); } return a; From cf1fff0845eff9114e921c7c921533ae1b1fb3c8 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 09:22:54 -0400 Subject: [PATCH 43/78] R Austin review: move formatting out of model --- .../security_solution/common/endpoint/models/event.ts | 6 +++--- .../resolver/view/panels/panel_content_related_detail.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index 46bddbec38b08..68fb18152a23d 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -81,11 +81,11 @@ export function primaryEventCategory(event: ResolverEvent): string { * see: https://www.elastic.co/guide/en/ecs/current/ecs-event.html * @param event The ResolverEvent to get the ecs type for */ -export function ecsEventType(event: ResolverEvent): string { +export function ecsEventType(event: ResolverEvent): string[] { if (isLegacyEvent(event)) { - return event.endgame.event_subtype_full || ''; + return [event.endgame.event_subtype_full || '']; } - return typeof event.event.type === 'string' ? event.event.type : event.event.type.join('/'); + return typeof event.event.type === 'string' ? [event.event.type] : event.event.type; } /** 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/panel_content_related_detail.tsx index b447b5e062ded..b578e4d236fae 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/panel_content_related_detail.tsx @@ -315,7 +315,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ id="xpack.securitySolution.enpoint.resolver.panel.relatedEventDetail.categoryAndType" values={{ category: relatedEventCategory, - eventType: event.ecsEventType(relatedEventToShowDetailsFor), + eventType: String(event.ecsEventType(relatedEventToShowDetailsFor)), }} defaultMessage="{category} {eventType}" /> From 1659805030c81f8a24759b0400fb2d7e65138f17 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 11:08:58 -0400 Subject: [PATCH 44/78] R Austin review: chip shot at model data --- .../common/endpoint/models/event.ts | 27 ++++++++++++------- .../panels/panel_content_related_detail.tsx | 23 +++++++++++++--- .../panels/panel_content_related_list.tsx | 12 +++++++-- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index 68fb18152a23d..b515b9b31c8e4 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -127,13 +127,17 @@ export function isRecordNamable( /** * Based on the ECS category of the event, attempt to provide a more descriptive name * (e.g. the `event.registry.key` for `registry` or the `dns.question.name` for `dns`, etc.). + * This function returns the data in the form of `{subject, descriptor}` where `subject` will + * tend to be the more distinctive term (e.g. 137.213.212.7 for a network event) and the + * `descriptor` can be used to present more useful/meaningful view (e.g. `inbound 137.213.212.7` + * in the example above). * see: https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html * @param event The ResolverEvent to get the descriptive name for * @returns { descriptiveName } An attempt at providing a readable name to the user */ -export function descriptiveName(event: ResolverEvent): string { +export function descriptiveName(event: ResolverEvent): { subject: string; descriptor?: string } { if (isLegacyEvent(event)) { - return eventName(event); + return { subject: eventName(event) }; } /** @@ -148,15 +152,18 @@ export function descriptiveName(event: ResolverEvent): string { if (namableNetworkEvent) { if (namableNetworkEvent.network.forwarded_ip) { - return `${ - namableNetworkEvent.network.direction ? `${namableNetworkEvent.network.direction} ` : '' - }${namableNetworkEvent.network.forwarded_ip}`; + return { + subject: String(namableNetworkEvent.network.forwarded_ip), + descriptor: String(namableNetworkEvent.network.direction), + }; } } if (namableFileEvent) { if (namableFileEvent.file.path) { - return String(namableFileEvent.file.path); + return { + subject: String(namableFileEvent.file.path), + }; } } @@ -164,15 +171,17 @@ export function descriptiveName(event: ResolverEvent): string { if (namableRegistryEvent) { const pathOrKey = namableRegistryEvent.registry.path || namableRegistryEvent.registry.key; if (pathOrKey) { - return String(pathOrKey); + return { + subject: String(pathOrKey), + }; } } if (namableDNSEvent) { if (namableDNSEvent.dns.question.name) { - return String(namableDNSEvent.dns.question.name); + return { subject: String(namableDNSEvent.dns.question.name) }; } } // Fall back on entityId if we can't fish a more descriptive name out. - return entityId(event); + return { subject: entityId(event) }; } 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/panel_content_related_detail.tsx index b578e4d236fae..ca4cfedd4f350 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/panel_content_related_detail.tsx @@ -205,6 +205,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ ]; }, [pushToQueryParams, eventsString]); + const { subject, descriptor = '' } = event.descriptiveName(relatedEventToShowDetailsFor!); const crumbs = useMemo(() => { return [ { @@ -251,9 +252,15 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ }, }, { - text: relatedEventToShowDetailsFor - ? event.descriptiveName(relatedEventToShowDetailsFor) - : naString, + text: relatedEventToShowDetailsFor ? ( + + ) : ( + naString + ), onClick: () => {}, }, ]; @@ -267,6 +274,8 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ naString, relatedEventCategory, relatedEventToShowDetailsFor, + subject, + descriptor, ]); /** @@ -327,7 +336,13 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ />
- {event.descriptiveName(relatedEventToShowDetailsFor)} + + + {sections.map(({ sectionTitle, entries }, index) => { return ( diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index ea2ce27085efe..84e9951cb9fde 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -30,7 +30,7 @@ interface MatchingEventEntry { formattedDate: string; eventType: string; eventCategory: string; - name: string; + name: { subject: string; descriptor?: string }; entityId: string; setQueryParams: () => void; } @@ -48,6 +48,7 @@ const DisplayList = memo(function DisplayList({ <> {matchingEventEntries.map((eventView, index) => { + const { subject, descriptor = '' } = eventView.name; return ( @@ -68,7 +69,13 @@ const DisplayList = memo(function DisplayList({ /> - {eventView.name} + + + {index === matchingEventEntries.length - 1 ? null : } ); @@ -155,6 +162,7 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr const eventTime = event.eventTimestamp(resolverEvent); const formattedDate = typeof eventTime === 'undefined' ? '' : formatDate(eventTime); const entityId = event.eventId(resolverEvent); + return { formattedDate, eventCategory: `${eventType}`, From 6ca100496ef8190e6992e39d18b85e06cb67b3f5 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 11:13:59 -0400 Subject: [PATCH 45/78] R Austin review: remove unnecessary guard --- .../resolver/view/panels/panel_content_process_detail.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b30c1c6c49cd0..bb154bbb46ce7 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 @@ -46,10 +46,10 @@ export const ProcessDetails = memo(function ProcessDetails({ processEvent: ResolverEvent; pushToQueryParams: (arg0: CrumbInfo) => unknown; }) { - const processName = processEvent && event.eventName(processEvent); + const processName = event.eventName(processEvent); const processInfoEntry = useMemo(() => { let dateTime = ''; - const eventTime = processEvent && event.eventTimestamp(processEvent); + const eventTime = event.eventTimestamp(processEvent); if (eventTime) { dateTime = formatDate(eventTime); } From 81a865d3e904ce4c47d407f89d3b8c696c2e04ae Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 11:17:37 -0400 Subject: [PATCH 46/78] R Austin review: prefer const --- .../resolver/view/panels/panel_content_process_detail.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 bb154bbb46ce7..8375e19c721ae 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 @@ -48,11 +48,8 @@ export const ProcessDetails = memo(function ProcessDetails({ }) { const processName = event.eventName(processEvent); const processInfoEntry = useMemo(() => { - let dateTime = ''; const eventTime = event.eventTimestamp(processEvent); - if (eventTime) { - dateTime = formatDate(eventTime); - } + const dateTime = eventTime ? formatDate(eventTime) : ''; const createdEntry = { title: i18n.translate( From a7cdb88ab14e42d111cca2e3d8ab24270c673248 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 11:56:17 -0400 Subject: [PATCH 47/78] R Austin review: reorganize/rename actions --- .../public/resolver/store/actions.ts | 21 ++++++++++++++++++- .../public/resolver/store/reducer.ts | 10 +++++++-- .../public/resolver/view/panel.tsx | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/store/actions.ts b/x-pack/plugins/security_solution/public/resolver/store/actions.ts index 0bab911553ea3..82971e8c83aa4 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/actions.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/actions.ts @@ -24,6 +24,24 @@ interface UserBroughtProcessIntoView { }; } +/** + * When an examination of query params in the UI indicates that state needs to + * be updated to reflect the new selection + */ +interface AppDetectedNewIdFromQueryParams { + readonly type: 'appDetectedNewIdFromQueryParams'; + readonly payload: { + /** + * Used to identify the process the process that should be synced with state. + */ + readonly process: ResolverEvent; + /** + * The time (since epoch in milliseconds) when the action was dispatched. + */ + readonly time: number; + }; +} + /** * Used when the alert list selects an alert and the flyout shows resolver. */ @@ -122,4 +140,5 @@ export type ResolverAction = | UserSelectedResolverNode | UserRequestedRelatedEventData | UserSelectedRelatedEventCategory - | UserSelectedRelatedAlerts; + | UserSelectedRelatedAlerts + | AppDetectedNewIdFromQueryParams; diff --git a/x-pack/plugins/security_solution/public/resolver/store/reducer.ts b/x-pack/plugins/security_solution/public/resolver/store/reducer.ts index c7376af6ce896..f48171353fbb8 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/reducer.ts @@ -37,7 +37,10 @@ const uiReducer: Reducer = ( selectedDescendantId: action.payload.nodeId, processEntityIdOfSelectedDescendant: action.payload.selectedProcessId, }; - } else if (action.type === 'userBroughtProcessIntoView') { + } else if ( + action.type === 'userBroughtProcessIntoView' || + action.type === 'appDetectedNewIdFromQueryParams' + ) { /** * This action has a process payload (instead of a processId), so we use * `uniquePidForProcess` and `resolverNodeIdGenerator` to resolve the determinant @@ -64,7 +67,10 @@ const concernReducers = combineReducers({ export const resolverReducer: Reducer = (state, action) => { const nextState = concernReducers(state, action); - if (action.type === 'userBroughtProcessIntoView') { + if ( + action.type === 'userBroughtProcessIntoView' || + action.type === 'appDetectedNewIdFromQueryParams' + ) { return animateProcessIntoView(nextState, action.payload.time, action.payload.process); } else { return nextState; 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 ee271f1edcf3f..7e241caa84478 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -94,7 +94,7 @@ const PanelContent = memo(function PanelContent() { ) { setLastUpdatedProcess(paramsSelectedEvent); dispatch({ - type: 'userBroughtProcessIntoView', + type: 'appDetectedNewIdFromQueryParams', payload: { time: timestamp(), process: paramsSelectedEvent, From 2445aba35f82af3c1ecc755529707b8dd78d7e1c Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 12:06:36 -0400 Subject: [PATCH 48/78] K Qualters review: fix pid/ppid references --- .../security_solution/public/resolver/models/process_event.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts index a827e682e29a9..07e466e634293 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts @@ -83,7 +83,7 @@ export function uniquePidForProcess(passedEvent: ResolverEvent): string { */ export function processPid(passedEvent: ResolverEvent): string { if (event.isLegacyEvent(passedEvent)) { - return String(passedEvent.endgame.unique_pid); + return String(passedEvent.endgame.pid); } else { return String(passedEvent.process.pid); } @@ -94,7 +94,7 @@ export function processPid(passedEvent: ResolverEvent): string { */ export function uniqueParentPidForProcess(passedEvent: ResolverEvent): string | undefined { if (event.isLegacyEvent(passedEvent)) { - return String(passedEvent.endgame.unique_ppid); + return String(passedEvent.endgame.ppid); } else { return passedEvent.process.parent?.entity_id; } From 28c8e92899ce54df0e58eaadb0a4c72db8375ba6 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 13:14:19 -0400 Subject: [PATCH 49/78] R Austin review: decouple view selection from render --- .../public/resolver/view/panel.tsx | 66 ++++++++++++------- 1 file changed, 43 insertions(+), 23 deletions(-) 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 7e241caa84478..943e0b093cdde 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -147,14 +147,14 @@ const PanelContent = memo(function PanelContent() { * for the table & breadcrumb nav. * */ - const whichTableViewAndBreadcrumbsToRender = useMemo(() => { + const panelToShow = useMemo(() => { if (crumbEvent === '' && crumbId === '') { /** * | Crumb/Table | &crumbId | &crumbEvent | * | :--------------------- | :------------------------- | :---------------------- | * | all processes/default | null | null | */ - return ; + return 'processListWithCounts'; } if (graphableProcessEntityIds.has(crumbId)) { @@ -164,9 +164,7 @@ const PanelContent = memo(function PanelContent() { * | process detail | entity_id of process | null | */ if (crumbEvent === '' && uiSelectedEvent) { - return ( - - ); + return 'processDetails'; } /** @@ -176,13 +174,7 @@ const PanelContent = memo(function PanelContent() { */ if (crumbEvent === 'all' && uiSelectedEvent) { - return ( - - ); + return 'eventCountsForProcess'; } /** @@ -192,14 +184,7 @@ const PanelContent = memo(function PanelContent() { */ if (crumbEvent in displayNameRecord && uiSelectedEvent) { - return ( - - ); + return 'processEventListNarrowedByType'; } } @@ -209,6 +194,42 @@ const PanelContent = memo(function PanelContent() { * | :--------------------- | :------------------------- | :---------------------- | * | related event detail | event_id of related event | entity_id of process | */ + return 'relatedEventDetail'; + } + + // The default 'Event List' / 'List of all processes' view + return 'processListWithCounts'; + }, [uiSelectedEvent, crumbEvent, crumbId, graphableProcessEntityIds]); + + const panelInstance = useMemo(() => { + if (panelToShow === 'processDetails') { + return ( + + ); + } + + if (panelToShow === 'eventCountsForProcess') { + return ( + + ); + } + + if (panelToShow === 'processEventListNarrowedByType') { + return ( + + ); + } + + if (panelToShow === 'relatedEventDetail') { const parentCount: number = Object.values( relatedStatsForIdFromParams?.events.byCategory || {} ).reduce((sum, val) => sum + val, 0); @@ -221,7 +242,6 @@ const PanelContent = memo(function PanelContent() { /> ); } - // The default 'Event List' / 'List of all processes' view return ; }, [ @@ -230,10 +250,10 @@ const PanelContent = memo(function PanelContent() { crumbId, pushToQueryParams, relatedStatsForIdFromParams, - graphableProcessEntityIds, + panelToShow, ]); - return <>{whichTableViewAndBreadcrumbsToRender}; + return <>{panelInstance}; }); PanelContent.displayName = 'PanelContent'; From 88628232f23c943fe7331926f496b0f153884186 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 14:22:51 -0400 Subject: [PATCH 50/78] Bug fix: No undefined siblings --- .../public/resolver/models/indexed_process_tree.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree.ts b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree.ts index 3102b450e54f8..db00ca2d59968 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree.ts @@ -55,9 +55,8 @@ export function factory(processes: ResolverEvent[]): IndexedProcessTree { currentProcessAdjacencyMap.parent = uniqueParentPid; } } else { - idToChildren.set(uniqueParentPid, [process]); - if (uniqueParentPid) { + idToChildren.set(uniqueParentPid, [process]); /** * Get the parent's map, otherwise set an empty one */ From feeadf34e89f807c4e457a59558ba8b46d12838d Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 14:34:29 -0400 Subject: [PATCH 51/78] Bug fix: reference error --- .../resolver/view/panels/panel_content_related_detail.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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/panel_content_related_detail.tsx index ca4cfedd4f350..ff3c80e1e7456 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/panel_content_related_detail.tsx @@ -205,7 +205,9 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ ]; }, [pushToQueryParams, eventsString]); - const { subject, descriptor = '' } = event.descriptiveName(relatedEventToShowDetailsFor!); + const { subject = '', descriptor = '' } = relatedEventToShowDetailsFor + ? event.descriptiveName(relatedEventToShowDetailsFor) + : {}; const crumbs = useMemo(() => { return [ { From fd4738872e35927f2bb7a617d5ce663e3e4e024f Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 14:58:21 -0400 Subject: [PATCH 52/78] R Austin review: use App- actions to sync state --- .../public/resolver/store/actions.ts | 14 ++++++++- .../public/resolver/store/reducer.ts | 6 ++++ .../public/resolver/store/selectors.ts | 5 ++++ .../public/resolver/store/ui/selectors.ts | 5 ++++ .../public/resolver/types.ts | 4 +++ .../public/resolver/view/panel.tsx | 30 +++++++++++++++---- 6 files changed, 57 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/store/actions.ts b/x-pack/plugins/security_solution/public/resolver/store/actions.ts index 82971e8c83aa4..83b6c6ae0cea4 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/actions.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/actions.ts @@ -24,6 +24,17 @@ interface UserBroughtProcessIntoView { }; } +/** + * Dispatched to notify state that a different panel needs to be displayed + */ +interface AppDisplayedDifferentPanel { + readonly type: 'appDisplayedDifferentPanel'; + /** + * The name of the panel to display + */ + readonly payload: string; +} + /** * When an examination of query params in the UI indicates that state needs to * be updated to reflect the new selection @@ -141,4 +152,5 @@ export type ResolverAction = | UserRequestedRelatedEventData | UserSelectedRelatedEventCategory | UserSelectedRelatedAlerts - | AppDetectedNewIdFromQueryParams; + | AppDetectedNewIdFromQueryParams + | AppDisplayedDifferentPanel; diff --git a/x-pack/plugins/security_solution/public/resolver/store/reducer.ts b/x-pack/plugins/security_solution/public/resolver/store/reducer.ts index f48171353fbb8..77dffd79ea094 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/reducer.ts @@ -23,6 +23,7 @@ const uiReducer: Reducer = ( activeDescendantId: null, selectedDescendantId: null, processEntityIdOfSelectedDescendant: null, + panelToDisplay: null, }, action ) => { @@ -37,6 +38,11 @@ const uiReducer: Reducer = ( selectedDescendantId: action.payload.nodeId, processEntityIdOfSelectedDescendant: action.payload.selectedProcessId, }; + } else if (action.type === 'appDisplayedDifferentPanel') { + return { + ...uiState, + panelToDisplay: action.payload, + }; } else if ( action.type === 'userBroughtProcessIntoView' || action.type === 'appDetectedNewIdFromQueryParams' 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 719eb7a730afa..bff30c62864f2 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/selectors.ts @@ -108,6 +108,11 @@ export const uiSelectedDescendantProcessId = composeSelectors( uiSelectors.selectedDescendantProcessId ); +/** + * The current panel to display + */ +export const currentPanelView = composeSelectors(uiStateSelector, uiSelectors.currentPanelView); + /** * Returns the camera state from within ResolverState */ diff --git a/x-pack/plugins/security_solution/public/resolver/store/ui/selectors.ts b/x-pack/plugins/security_solution/public/resolver/store/ui/selectors.ts index 494d8884329c6..bddc7d34abf1c 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/ui/selectors.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/ui/selectors.ts @@ -39,3 +39,8 @@ export const selectedDescendantProcessId = createSelector( return processEntityIdOfSelectedDescendant; } ); + +// Select the current panel to be displayed +export const currentPanelView = (uiState: ResolverUIState) => { + return uiState.panelToDisplay; +}; diff --git a/x-pack/plugins/security_solution/public/resolver/types.ts b/x-pack/plugins/security_solution/public/resolver/types.ts index 789328876cd7b..a48f3b59b0f6d 100644 --- a/x-pack/plugins/security_solution/public/resolver/types.ts +++ b/x-pack/plugins/security_solution/public/resolver/types.ts @@ -50,6 +50,10 @@ export interface ResolverUIState { * The entity_id of the process for the resolver's currently selected descendant. */ readonly processEntityIdOfSelectedDescendant: string | null; + /** + * Which panel the ui should display + */ + readonly panelToDisplay: string | null; } /** 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 943e0b093cdde..4bef2f4d2a10e 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.tsx @@ -4,7 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { memo, useCallback, useMemo, useContext, useLayoutEffect, useState } from 'react'; +import React, { + memo, + useCallback, + useMemo, + useContext, + useLayoutEffect, + useState, + useEffect, +} from 'react'; import { useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; // eslint-disable-next-line import/no-nodejs-modules @@ -201,14 +209,24 @@ const PanelContent = memo(function PanelContent() { return 'processListWithCounts'; }, [uiSelectedEvent, crumbEvent, crumbId, graphableProcessEntityIds]); + useEffect(() => { + // dispatch `appDisplayedDifferentPanel` to sync state with which panel gets displayed + dispatch({ + type: 'appDisplayedDifferentPanel', + payload: panelToShow, + }); + }, [panelToShow, dispatch]); + + const currentPanelView = useSelector(selectors.currentPanelView); + const panelInstance = useMemo(() => { - if (panelToShow === 'processDetails') { + if (currentPanelView === 'processDetails') { return ( ); } - if (panelToShow === 'eventCountsForProcess') { + if (currentPanelView === 'eventCountsForProcess') { return ( sum + val, 0); @@ -250,7 +268,7 @@ const PanelContent = memo(function PanelContent() { crumbId, pushToQueryParams, relatedStatsForIdFromParams, - panelToShow, + currentPanelView, ]); return <>{panelInstance}; From 07f90829dfc222ae872cfa6cc6305e44910524a8 Mon Sep 17 00:00:00 2001 From: oatkiller Date: Wed, 17 Jun 2020 15:47:12 -0400 Subject: [PATCH 53/78] change descriptive name code to use deep partial --- .../common/endpoint/models/event.ts | 80 +++++-------------- .../common/endpoint/types.ts | 9 +++ 2 files changed, 31 insertions(+), 58 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index b515b9b31c8e4..dbb0b4a17eb25 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -95,35 +95,7 @@ export function ecsEventType(event: ResolverEvent): string[] { * There are drawbacks to trying to do this: It *will* require ongoing maintenance. It presents temptations to overarticulate. * On balance, however, it seems that the benefit of giving the user some form of information they can recognize & scan outweighs the drawbacks. */ - -/** - * A type that annotates the basic requirements for giving the event a `descriptive name` - */ -type detailRecord = T extends 'registry' - ? { path: unknown; key: unknown } - : T extends 'dns' - ? { question: { name: unknown } } - : T extends 'network' - ? { direction: unknown; forwarded_ip: unknown } - : T extends 'file' - ? { path: unknown } - : unknown; -type ecsRecordWithType = Record>; - -/** - * Verify that the `ecsCategory` exists as a key on the event record. i.e. if ecsCategory is `registry`, the event is shaped like {registry: object} - * - * @param event - * @param ecsCategory - */ -export function isRecordNamable( - event: object, - ecsCategory: T -): event is ecsRecordWithType { - // The goal here is to reach in and grab a better name for the event (if one exists) without being too obtrusive/assertive about ECS - return typeof (event as ecsRecordWithType)[ecsCategory] === 'object'; -} - +type DeepPartial = T extends object ? { [K in keyof T]?: DeepPartial } : T; /** * Based on the ECS category of the event, attempt to provide a more descriptive name * (e.g. the `event.registry.key` for `registry` or the `dns.question.name` for `dns`, etc.). @@ -140,46 +112,38 @@ export function descriptiveName(event: ResolverEvent): { subject: string; descri return { subject: eventName(event) }; } + // To be somewhat defensive, we'll check for the presence of these. + const partialEvent: DeepPartial = event; + /** * This list of attempts can be expanded/adjusted as the underlying model changes over time: */ // Stable, per ECS 1.5: https://www.elastic.co/guide/en/ecs/current/ecs-allowed-values-event-category.html - const namableNetworkEvent = isRecordNamable(event, 'network') && event; - const namableFileEvent = isRecordNamable(event, 'file') && event; - const namableRegistryEvent = isRecordNamable(event, 'registry') && event; - const namableDNSEvent = isRecordNamable(event, 'dns') && event; - - if (namableNetworkEvent) { - if (namableNetworkEvent.network.forwarded_ip) { - return { - subject: String(namableNetworkEvent.network.forwarded_ip), - descriptor: String(namableNetworkEvent.network.direction), - }; - } + + if (partialEvent.network?.forwarded_ip) { + return { + subject: String(partialEvent.network?.forwarded_ip), + descriptor: String(partialEvent.network?.direction), + }; } - if (namableFileEvent) { - if (namableFileEvent.file.path) { - return { - subject: String(namableFileEvent.file.path), - }; - } + if (partialEvent.file?.path) { + return { + subject: String(partialEvent.file?.path), + }; } // Extended categories (per ECS 1.5): - if (namableRegistryEvent) { - const pathOrKey = namableRegistryEvent.registry.path || namableRegistryEvent.registry.key; - if (pathOrKey) { - return { - subject: String(pathOrKey), - }; - } + const pathOrKey = partialEvent.registry?.path || partialEvent.registry?.key; + if (pathOrKey) { + return { + subject: String(pathOrKey), + }; } - if (namableDNSEvent) { - if (namableDNSEvent.dns.question.name) { - return { subject: String(namableDNSEvent.dns.question.name) }; - } + + if (partialEvent.dns?.question?.name) { + return { subject: String(partialEvent.dns?.question?.name) }; } // Fall back on entityId if we can't fish a more descriptive name out. diff --git a/x-pack/plugins/security_solution/common/endpoint/types.ts b/x-pack/plugins/security_solution/common/endpoint/types.ts index cab493260ce80..b52bf310f60ba 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types.ts @@ -438,6 +438,13 @@ export interface EndpointEvent { kind: string; }; host: Host; + network: { + direction: unknown; + forwarded_ip: unknown; + }; + dns: { + question: { name: unknown }; + }; process: { entity_id: string; name: string; @@ -461,6 +468,8 @@ export interface EndpointEvent { domain?: string; name: string; }; + file: { path: unknown }; + registry: { path: unknown; key: unknown }; } export type ResolverEvent = EndpointEvent | LegacyEndpointEvent; From 66ab2513662f768007f18471cd909f28ffd76b64 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 16:28:39 -0400 Subject: [PATCH 54/78] TS type --- .../plugins/security_solution/common/endpoint/generate_data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index 23ad00ef9f463..12e6d1c8b8c6d 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -514,7 +514,7 @@ export class EndpointDocGenerator { domain: this.randomString(10), name: this.randomString(10), }, - }; + } as EndpointEvent; } /** From 0ca4fb1eaa1f1d2928c1b78e6db4966dbef16bb4 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 16:47:58 -0400 Subject: [PATCH 55/78] K Qualters review: fix pid/ppid again --- .../security_solution/public/resolver/models/process_event.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts index 07e466e634293..bf016a9a9ad5c 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts @@ -94,7 +94,7 @@ export function processPid(passedEvent: ResolverEvent): string { */ export function uniqueParentPidForProcess(passedEvent: ResolverEvent): string | undefined { if (event.isLegacyEvent(passedEvent)) { - return String(passedEvent.endgame.ppid); + return String(passedEvent.endgame.unique_pid); } else { return passedEvent.process.parent?.entity_id; } @@ -105,7 +105,7 @@ export function uniqueParentPidForProcess(passedEvent: ResolverEvent): string | */ export function processParentPid(passedEvent: ResolverEvent): number | undefined { if (event.isLegacyEvent(passedEvent)) { - return passedEvent.endgame.unique_ppid; + return passedEvent.endgame.ppid; } else { const ppid = passedEvent.process.parent?.pid; return ppid ? ppid : undefined; From 6d76057cacadd3285c58a967e9ea90ded25d1c6f Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 16:50:36 -0400 Subject: [PATCH 56/78] R Austin review: fix ecsEventType --- .../plugins/security_solution/common/endpoint/models/event.ts | 4 ++-- .../security_solution/public/resolver/models/process_event.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index dbb0b4a17eb25..4d3c5baf696c9 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -81,9 +81,9 @@ export function primaryEventCategory(event: ResolverEvent): string { * see: https://www.elastic.co/guide/en/ecs/current/ecs-event.html * @param event The ResolverEvent to get the ecs type for */ -export function ecsEventType(event: ResolverEvent): string[] { +export function ecsEventType(event: ResolverEvent): Array { if (isLegacyEvent(event)) { - return [event.endgame.event_subtype_full || '']; + return [event.endgame.event_subtype_full]; } return typeof event.event.type === 'string' ? [event.event.type] : event.event.type; } diff --git a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts index bf016a9a9ad5c..667e555bd749c 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts @@ -94,7 +94,7 @@ export function processPid(passedEvent: ResolverEvent): string { */ export function uniqueParentPidForProcess(passedEvent: ResolverEvent): string | undefined { if (event.isLegacyEvent(passedEvent)) { - return String(passedEvent.endgame.unique_pid); + return String(passedEvent.endgame.unique_ppid); } else { return passedEvent.process.parent?.entity_id; } From 89bd6a862b9b9b9903726a2130ef2db681fd1068 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 17:00:38 -0400 Subject: [PATCH 57/78] R Austin review: refactor state change --- .../security_solution/public/resolver/store/data/reducer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts b/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts index d7db4ed9bb20a..1fc50042824eb 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts @@ -41,13 +41,13 @@ export const dataReducer: Reducer = (state = initialS } else if (action.type === 'userRequestedRelatedEventData') { return { ...state, - relatedEventsReady: new Map(state.relatedEventsReady.set(action.payload, false)), + relatedEventsReady: new Map([...state.relatedEventsReady, [action.payload, false]]), }; } else if (action.type === 'serverReturnedRelatedEventData') { return { ...state, - relatedEventsReady: new Map(state.relatedEventsReady.set(action.payload.entityID, true)), - relatedEvents: new Map(state.relatedEvents.set(action.payload.entityID, action.payload)), + relatedEventsReady: new Map([...state.relatedEventsReady, [action.payload.entityID, true]]), + relatedEvents: new Map([...state.relatedEvents, [action.payload.entityID, action.payload]]), }; } else { return state; From 1058e680726ef3f49b925e610a67cf750a43c827 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 17:11:50 -0400 Subject: [PATCH 58/78] R Austin review: new action for missing relateds --- .../public/resolver/store/actions.ts | 12 +++++++++++- .../public/resolver/store/data/reducer.ts | 5 ++++- .../public/resolver/store/middleware.ts | 6 +++++- .../view/panels/panel_content_related_detail.tsx | 2 +- .../view/panels/panel_content_related_list.tsx | 2 +- 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/store/actions.ts b/x-pack/plugins/security_solution/public/resolver/store/actions.ts index 83b6c6ae0cea4..c633d791e8bf2 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/actions.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/actions.ts @@ -82,6 +82,15 @@ interface UserRequestedRelatedEventData { readonly payload: string; } +/** + * The action dispatched when the app requests related event data for one + * subject (whose entity_id should be included as `payload`) + */ +interface AppDetectedMissingEventData { + readonly type: 'appDetectedMissingEventData'; + readonly payload: string; +} + /** * When the user switches the "active descendant" of the Resolver. * The "active descendant" (from the point of view of the parent element) @@ -153,4 +162,5 @@ export type ResolverAction = | UserSelectedRelatedEventCategory | UserSelectedRelatedAlerts | AppDetectedNewIdFromQueryParams - | AppDisplayedDifferentPanel; + | AppDisplayedDifferentPanel + | AppDetectedMissingEventData; diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts b/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts index 1fc50042824eb..3e897a91a74c6 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts @@ -38,7 +38,10 @@ export const dataReducer: Reducer = (state = initialS ...state, hasError: true, }; - } else if (action.type === 'userRequestedRelatedEventData') { + } else if ( + action.type === 'userRequestedRelatedEventData' || + action.type === 'appDetectedMissingEventData' + ) { return { ...state, relatedEventsReady: new Map([...state.relatedEventsReady, [action.payload, false]]), diff --git a/x-pack/plugins/security_solution/public/resolver/store/middleware.ts b/x-pack/plugins/security_solution/public/resolver/store/middleware.ts index e840f045186d4..7f6f58dac7158 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/middleware.ts @@ -93,7 +93,11 @@ export const resolverMiddlewareFactory: MiddlewareFactory = (context) => { }); } } - } else if (action.type === 'userRequestedRelatedEventData' && context) { + } else if ( + (action.type === 'userRequestedRelatedEventData' || + action.type === 'appDetectedMissingEventData') && + context + ) { const entityIdToFetchFor = action.payload; let result: ResolverRelatedEvents; try { 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/panel_content_related_detail.tsx index ff3c80e1e7456..158ebc3dc831f 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/panel_content_related_detail.tsx @@ -136,7 +136,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ useEffect(() => { if (typeof relatedsReady === 'undefined') { dispatch({ - type: 'userRequestedRelatedEventData', + type: 'appDetectedMissingEventData', payload: processEntityId, }); } diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index 84e9951cb9fde..abfa7916aaa05 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -126,7 +126,7 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr useEffect(() => { if (typeof relatedsReady === 'undefined') { dispatch({ - type: 'userRequestedRelatedEventData', + type: 'appDetectedMissingEventData', payload: processEntityId, }); } From 318eba28676db62960866e5fccb4558f918a5fb0 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 17:21:01 -0400 Subject: [PATCH 59/78] R Austin review: Add comments to exports --- .../security_solution/public/resolver/view/assets.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/x-pack/plugins/security_solution/public/resolver/view/assets.tsx b/x-pack/plugins/security_solution/public/resolver/view/assets.tsx index ef65f11575dd1..82f969b755b2f 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/assets.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/assets.tsx @@ -417,6 +417,12 @@ const processTypeToCube: Record = { unknownEvent: 'runningProcessCube', }; +/** + * This will return which type the ResolverEvent will display as in the Node component + * it will be something like 'runningProcessCube' or 'terminatedProcessCube' + * + * @param processEvent {ResolverEvent} the event to get the Resolver Component Node type of + */ export function nodeType(processEvent: ResolverEvent): keyof NodeStyleMap { const processType = processModel.eventType(processEvent); if (processType in processTypeToCube) { @@ -425,6 +431,9 @@ export function nodeType(processEvent: ResolverEvent): keyof NodeStyleMap { return 'runningProcessCube'; } +/** + * A hook to bring Resolver theming information into components. + */ export const useResolverTheme = (): { colorMap: ColorMap; nodeAssets: NodeStyleMap; From 6c20672a26cd8b93968b992fbdbc15a57b776b06 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 17:26:46 -0400 Subject: [PATCH 60/78] R Austin review: Comment on aggregate totals --- .../view/panels/panel_content_related_counts.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) 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/panel_content_related_counts.tsx index 218014c885227..08ee89f070e4c 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/panel_content_related_counts.tsx @@ -41,6 +41,16 @@ export const EventCountsForProcess = memo(function EventCountsForProcess({ 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 From 7a0c375aaae501f95cbb1d48690d69a1b45ae377 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 17:32:46 -0400 Subject: [PATCH 61/78] R Austin review: export BoldCode --- .../panels/panel_content_related_detail.tsx | 17 ++--------------- .../view/panels/panel_content_related_list.tsx | 3 +-- .../view/panels/panel_content_utilities.tsx | 11 ++++++++++- 3 files changed, 13 insertions(+), 18 deletions(-) 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/panel_content_related_detail.tsx index 158ebc3dc831f..bba023abf3e75 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/panel_content_related_detail.tsx @@ -6,30 +6,17 @@ import React, { memo, useMemo, useEffect, Fragment } from 'react'; import { i18n } from '@kbn/i18n'; -import { - EuiSpacer, - EuiText, - EuiDescriptionList, - EuiCode, - EuiTextColor, - EuiTitle, -} from '@elastic/eui'; +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 } from './panel_content_utilities'; +import { CrumbInfo, formatDate, StyledBreadcrumbs, BoldCode } 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'; -export const BoldCode = styled(EuiCode)` - &.euiCodeBlock code.euiCodeBlock__code { - font-weight: 900; - } -`; - /** * A helper function to turn objects into EuiDescriptionList entries. * This reflects the strategy of more or less "dumping" metadata for related processes diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index abfa7916aaa05..07357e2ca1d72 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -9,9 +9,8 @@ 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 { CrumbInfo, formatDate, StyledBreadcrumbs } from './panel_content_utilities'; +import { CrumbInfo, formatDate, StyledBreadcrumbs, BoldCode } from './panel_content_utilities'; import * as event from '../../../../common/endpoint/models/event'; -import { BoldCode } from './panel_content_related_detail'; import { ResolverEvent, ResolverNodeStats } from '../../../../common/endpoint/types'; import * as selectors from '../../store/selectors'; import { useResolverDispatch } from '../use_resolver_dispatch'; diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx index 3a6560475f917..781caf3fa44ca 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx @@ -5,11 +5,20 @@ */ import { i18n } from '@kbn/i18n'; -import { EuiBreadcrumbs, Breadcrumb } from '@elastic/eui'; +import { EuiBreadcrumbs, Breadcrumb, EuiCode } from '@elastic/eui'; import styled from 'styled-components'; import React, { memo } from 'react'; import { useResolverTheme } from '../assets'; +/** + * A bold version of EuiCode to display certain titles with + */ +export const BoldCode = styled(EuiCode)` + &.euiCodeBlock code.euiCodeBlock__code { + font-weight: 900; + } +`; + /** * The two query parameters we read/write on to control which view the table presents: */ From a75d74dc1d4d759d998269b74fad14f4770c4737 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 17:40:00 -0400 Subject: [PATCH 62/78] R Austin review: add comment to description list helper --- .../resolver/view/panels/panel_content_related_detail.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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/panel_content_related_detail.tsx index bba023abf3e75..e5101f825b7c2 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/panel_content_related_detail.tsx @@ -24,13 +24,16 @@ import { PanelContentError } from './panel_content_error'; * 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}/` : ''; + 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}` }; From 436ae12c77be6ae2f3a2981daaf8d3b4d773f23c Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 17:48:59 -0400 Subject: [PATCH 63/78] R Austin review: debride event model function --- .../security_solution/common/endpoint/models/event.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index 4d3c5baf696c9..3f07bf77abf20 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -56,9 +56,8 @@ export function parentEntityId(event: ResolverEvent): string | undefined { /** * @param event The event to get the category for */ -export function primaryEventCategory(event: ResolverEvent): string { +export function primaryEventCategory(event: ResolverEvent): string | undefined { // 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) { @@ -66,14 +65,10 @@ export function primaryEventCategory(event: ResolverEvent): string { } } else { const eventCategories = event.event.category; - const category = - typeof eventCategories === 'string' ? eventCategories : eventCategories[0] || ''; + const category = typeof eventCategories === 'string' ? eventCategories : eventCategories[0]; - if (category) { - eventCategoryToReturn = category; - } + return category; } - return eventCategoryToReturn; } /** From 7229e504b079f45fc8327140e46b1fa27fa0f663 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Wed, 17 Jun 2020 19:40:39 -0400 Subject: [PATCH 64/78] CI: Typecheck --- x-pack/plugins/security_solution/common/endpoint/types.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/types.ts b/x-pack/plugins/security_solution/common/endpoint/types.ts index b52bf310f60ba..471e3ca3dd161 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types.ts @@ -438,11 +438,11 @@ export interface EndpointEvent { kind: string; }; host: Host; - network: { + network?: { direction: unknown; forwarded_ip: unknown; }; - dns: { + dns?: { question: { name: unknown }; }; process: { @@ -464,12 +464,12 @@ export interface EndpointEvent { pid: number; }; }; - user: { + user?: { domain?: string; name: string; }; file: { path: unknown }; - registry: { path: unknown; key: unknown }; + registry?: { path: unknown; key: unknown }; } export type ResolverEvent = EndpointEvent | LegacyEndpointEvent; From cc5858a7d21a90e06940e530a821cd27ec080092 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 08:07:00 -0400 Subject: [PATCH 65/78] R Austin review: allow 0 as pid --- .../plugins/security_solution/common/endpoint/generate_data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index 12e6d1c8b8c6d..21565ac19dd0d 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -493,7 +493,7 @@ export class EndpointDocGenerator { }, host: this.commonInfo.host, process: { - pid: options.pid ? options.pid : this.randomN(5000), + pid: 'pid' in options ? options.pid : this.randomN(5000), executable: `C:\\${processName}`, args: `"C:\\${processName}" \\${this.randomString(3)}`, code_signature: { From ef3a5394196bce4de4e6b1f7902e44649a4cb5e5 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 08:14:32 -0400 Subject: [PATCH 66/78] R Austin review: remove EndpointEvent cast --- .../security_solution/common/endpoint/generate_data.ts | 5 +++-- x-pack/plugins/security_solution/common/endpoint/types.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index 21565ac19dd0d..9d18cd4207689 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -493,7 +493,8 @@ export class EndpointDocGenerator { }, host: this.commonInfo.host, process: { - pid: 'pid' in options ? options.pid : this.randomN(5000), + pid: + 'pid' in options && typeof options.pid !== 'undefined' ? options.pid : this.randomN(5000), executable: `C:\\${processName}`, args: `"C:\\${processName}" \\${this.randomString(3)}`, code_signature: { @@ -514,7 +515,7 @@ export class EndpointDocGenerator { domain: this.randomString(10), name: this.randomString(10), }, - } as EndpointEvent; + }; } /** diff --git a/x-pack/plugins/security_solution/common/endpoint/types.ts b/x-pack/plugins/security_solution/common/endpoint/types.ts index 471e3ca3dd161..cd9bb7e4ff599 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types.ts @@ -468,7 +468,7 @@ export interface EndpointEvent { domain?: string; name: string; }; - file: { path: unknown }; + file?: { path: unknown }; registry?: { path: unknown; key: unknown }; } From 074bfc3a9f98077730997168ca66913a308fc935 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 09:04:18 -0400 Subject: [PATCH 67/78] R Austin review: allow 0 as parentPid --- .../security_solution/common/endpoint/generate_data.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index 9d18cd4207689..499c300ebfc69 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -506,7 +506,10 @@ export class EndpointDocGenerator { parent: options.parentEntityID ? { entity_id: options.parentEntityID, - pid: options.parentPid ? options.parentPid : this.randomN(5000), + pid: + 'parentPid' in options && typeof options.parentPid !== 'undefined' + ? options.parentPid + : this.randomN(5000), } : undefined, name: processName, From 44a4d5680c6f0e2f8acca7bab500048b310c6f9b Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 09:08:00 -0400 Subject: [PATCH 68/78] R Austin review: simplify ppid return --- .../security_solution/public/resolver/models/process_event.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts index 667e555bd749c..93c8d92ef1041 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts @@ -107,8 +107,7 @@ export function processParentPid(passedEvent: ResolverEvent): number | undefined if (event.isLegacyEvent(passedEvent)) { return passedEvent.endgame.ppid; } else { - const ppid = passedEvent.process.parent?.pid; - return ppid ? ppid : undefined; + return passedEvent.process.parent?.pid; } } From 531070ad3a40510b538c0f6d6ea9f7cd396e12cf Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 09:14:51 -0400 Subject: [PATCH 69/78] R Austin review: make processPid consistent --- .../public/resolver/models/process_event.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts index 93c8d92ef1041..1094fee6da249 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/process_event.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/process_event.ts @@ -81,11 +81,11 @@ export function uniquePidForProcess(passedEvent: ResolverEvent): string { /** * Returns the pid for the process on the host */ -export function processPid(passedEvent: ResolverEvent): string { +export function processPid(passedEvent: ResolverEvent): number | undefined { if (event.isLegacyEvent(passedEvent)) { - return String(passedEvent.endgame.pid); + return passedEvent.endgame.pid; } else { - return String(passedEvent.process.pid); + return passedEvent.process.pid; } } From 56db0197b958dc74e43140d5a0f3d52776d1858e Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 09:37:45 -0400 Subject: [PATCH 70/78] test file got lost in rebase --- .../common/endpoint/models/event.test.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 x-pack/plugins/security_solution/common/endpoint/models/event.test.ts diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts new file mode 100644 index 0000000000000..ff32ffb86d544 --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts @@ -0,0 +1,39 @@ +/* + * 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 { EndpointDocGenerator } from '../generate_data'; +import { descriptiveName } from './event'; + +describe('Event descriptive names', () => { + let generator: EndpointDocGenerator; + beforeEach(() => { + generator = new EndpointDocGenerator('seed'); + }); + + it('returns the right name for a registry event', () => { + const extensions = { registry: { key: `HKLM/Windows/Software/abc` } }; + const event = generator.generateEvent({ eventCategory: 'registry', extensions }); + expect(descriptiveName(event)).toEqual(`HKLM/Windows/Software/abc`); + }); + + it('returns the right name for a network event', () => { + const randomIP = `${generator.randomIP()}`; + const extensions = { network: { direction: 'outbound', forwarded_ip: randomIP } }; + const event = generator.generateEvent({ eventCategory: 'network', extensions }); + expect(descriptiveName(event)).toEqual(`outbound ${randomIP}`); + }); + + it('returns the right name for a file event', () => { + const extensions = { file: { path: 'C:\\My Documents\\business\\January\\processName' } }; + const event = generator.generateEvent({ eventCategory: 'file', extensions }); + expect(descriptiveName(event)).toEqual('C:\\My Documents\\business\\January\\processName'); + }); + + it('returns the right name for a dns event', () => { + const extensions = { dns: { question: { name: `${generator.randomIP()}` } } }; + const event = generator.generateEvent({ eventCategory: 'dns', extensions }); + expect(descriptiveName(event)).toEqual(extensions.dns.question.name); + }); +}); From b203e4190e3d1c93009544d4054d83df31ab481b Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 09:47:47 -0400 Subject: [PATCH 71/78] fix test --- .../common/endpoint/models/event.test.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts index ff32ffb86d544..a0bf00f0274e6 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts @@ -15,25 +15,27 @@ describe('Event descriptive names', () => { it('returns the right name for a registry event', () => { const extensions = { registry: { key: `HKLM/Windows/Software/abc` } }; const event = generator.generateEvent({ eventCategory: 'registry', extensions }); - expect(descriptiveName(event)).toEqual(`HKLM/Windows/Software/abc`); + expect(descriptiveName(event)).toEqual({ subject: `HKLM/Windows/Software/abc` }); }); it('returns the right name for a network event', () => { const randomIP = `${generator.randomIP()}`; const extensions = { network: { direction: 'outbound', forwarded_ip: randomIP } }; const event = generator.generateEvent({ eventCategory: 'network', extensions }); - expect(descriptiveName(event)).toEqual(`outbound ${randomIP}`); + expect(descriptiveName(event)).toEqual({ subject: `${randomIP}`, descriptor: 'outbound' }); }); it('returns the right name for a file event', () => { const extensions = { file: { path: 'C:\\My Documents\\business\\January\\processName' } }; const event = generator.generateEvent({ eventCategory: 'file', extensions }); - expect(descriptiveName(event)).toEqual('C:\\My Documents\\business\\January\\processName'); + expect(descriptiveName(event)).toEqual({ + subject: 'C:\\My Documents\\business\\January\\processName', + }); }); it('returns the right name for a dns event', () => { const extensions = { dns: { question: { name: `${generator.randomIP()}` } } }; const event = generator.generateEvent({ eventCategory: 'dns', extensions }); - expect(descriptiveName(event)).toEqual(extensions.dns.question.name); + expect(descriptiveName(event)).toEqual({ subject: extensions.dns.question.name }); }); }); From 7cc3d728e07f9de837365428e854f52a4e96d529 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 10:12:25 -0400 Subject: [PATCH 72/78] CI: typecheck events --- .../security_solution/common/endpoint/types.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/types.ts b/x-pack/plugins/security_solution/common/endpoint/types.ts index cd9bb7e4ff599..9300c3cdbe1bb 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types.ts @@ -448,20 +448,20 @@ export interface EndpointEvent { process: { entity_id: string; name: string; - executable: string; - args: string; - code_signature: { + executable?: string; + args?: string; + code_signature?: { status: string; subject_name: string; }; - pid: number; - hash: { + pid?: number; + hash?: { md5: string; }; parent?: { entity_id: string; name?: string; - pid: number; + pid?: number; }; }; user?: { From 4fef5032a42572e9d8bf799cd6b7813e2ad98a7c Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 11:07:25 -0400 Subject: [PATCH 73/78] CI: typecheck fix --- x-pack/plugins/security_solution/common/endpoint/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/types.ts b/x-pack/plugins/security_solution/common/endpoint/types.ts index 9300c3cdbe1bb..b839f3b41772e 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types.ts @@ -451,7 +451,7 @@ export interface EndpointEvent { executable?: string; args?: string; code_signature?: { - status: string; + status?: string; subject_name: string; }; pid?: number; From ed73b46ab455e9106d34ce5cb2c947014a7d5a55 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 11:13:06 -0400 Subject: [PATCH 74/78] R Austin review: spelling in translation titles --- .../resolver/view/process_event_dot.tsx | 91 ++++++++++--------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx index c5002cb71688e..78b70611a6972 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx @@ -27,138 +27,147 @@ import { CrumbInfo } from './panels/panel_content_utilities'; */ export const displayNameRecord = { application: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.applicationEventTypeDisplayName', + 'xpack.securitySolution.endpoint.resolver.applicationEventTypeDisplayName', { defaultMessage: 'Application', } ), - apm: i18n.translate('xpack.securitySolution.enpoint.resolver.apmEventTypeDisplayName', { + apm: i18n.translate('xpack.securitySolution.endpoint.resolver.apmEventTypeDisplayName', { defaultMessage: 'APM', }), - audit: i18n.translate('xpack.securitySolution.enpoint.resolver.auditEventTypeDisplayName', { + audit: i18n.translate('xpack.securitySolution.endpoint.resolver.auditEventTypeDisplayName', { defaultMessage: 'Audit', }), authentication: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.authenticationEventTypeDisplayName', + 'xpack.securitySolution.endpoint.resolver.authenticationEventTypeDisplayName', { defaultMessage: 'Authentication', } ), certificate: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.certificateEventTypeDisplayName', + 'xpack.securitySolution.endpoint.resolver.certificateEventTypeDisplayName', { defaultMessage: 'Certificate', } ), - cloud: i18n.translate('xpack.securitySolution.enpoint.resolver.cloudEventTypeDisplayName', { + cloud: i18n.translate('xpack.securitySolution.endpoint.resolver.cloudEventTypeDisplayName', { defaultMessage: 'Cloud', }), - database: i18n.translate('xpack.securitySolution.enpoint.resolver.databaseEventTypeDisplayName', { - defaultMessage: 'Database', - }), - driver: i18n.translate('xpack.securitySolution.enpoint.resolver.driverEventTypeDisplayName', { + database: i18n.translate( + 'xpack.securitySolution.endpoint.resolver.databaseEventTypeDisplayName', + { + defaultMessage: 'Database', + } + ), + driver: i18n.translate('xpack.securitySolution.endpoint.resolver.driverEventTypeDisplayName', { defaultMessage: 'Driver', }), - email: i18n.translate('xpack.securitySolution.enpoint.resolver.emailEventTypeDisplayName', { + email: i18n.translate('xpack.securitySolution.endpoint.resolver.emailEventTypeDisplayName', { defaultMessage: 'Email', }), - file: i18n.translate('xpack.securitySolution.enpoint.resolver.fileEventTypeDisplayName', { + file: i18n.translate('xpack.securitySolution.endpoint.resolver.fileEventTypeDisplayName', { defaultMessage: 'File', }), - host: i18n.translate('xpack.securitySolution.enpoint.resolver.hostEventTypeDisplayName', { + host: i18n.translate('xpack.securitySolution.endpoint.resolver.hostEventTypeDisplayName', { defaultMessage: 'Host', }), - iam: i18n.translate('xpack.securitySolution.enpoint.resolver.iamEventTypeDisplayName', { + iam: i18n.translate('xpack.securitySolution.endpoint.resolver.iamEventTypeDisplayName', { defaultMessage: 'IAM', }), iam_group: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.iam_groupEventTypeDisplayName', + 'xpack.securitySolution.endpoint.resolver.iam_groupEventTypeDisplayName', { defaultMessage: 'IAM Group', } ), intrusion_detection: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.intrusion_detectionEventTypeDisplayName', + 'xpack.securitySolution.endpoint.resolver.intrusion_detectionEventTypeDisplayName', { defaultMessage: 'Intrusion Detection', } ), - malware: i18n.translate('xpack.securitySolution.enpoint.resolver.malwareEventTypeDisplayName', { + malware: i18n.translate('xpack.securitySolution.endpoint.resolver.malwareEventTypeDisplayName', { defaultMessage: 'Malware', }), network_flow: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.network_flowEventTypeDisplayName', + 'xpack.securitySolution.endpoint.resolver.network_flowEventTypeDisplayName', { defaultMessage: 'Network Flow', } ), - network: i18n.translate('xpack.securitySolution.enpoint.resolver.networkEventTypeDisplayName', { + network: i18n.translate('xpack.securitySolution.endpoint.resolver.networkEventTypeDisplayName', { defaultMessage: 'Network', }), - package: i18n.translate('xpack.securitySolution.enpoint.resolver.packageEventTypeDisplayName', { + package: i18n.translate('xpack.securitySolution.endpoint.resolver.packageEventTypeDisplayName', { defaultMessage: 'Package', }), - process: i18n.translate('xpack.securitySolution.enpoint.resolver.processEventTypeDisplayName', { + process: i18n.translate('xpack.securitySolution.endpoint.resolver.processEventTypeDisplayName', { defaultMessage: 'Process', }), - registry: i18n.translate('xpack.securitySolution.enpoint.resolver.registryEventTypeDisplayName', { - defaultMessage: 'Registry', - }), - session: i18n.translate('xpack.securitySolution.enpoint.resolver.sessionEventTypeDisplayName', { + registry: i18n.translate( + 'xpack.securitySolution.endpoint.resolver.registryEventTypeDisplayName', + { + defaultMessage: 'Registry', + } + ), + session: i18n.translate('xpack.securitySolution.endpoint.resolver.sessionEventTypeDisplayName', { defaultMessage: 'Session', }), - service: i18n.translate('xpack.securitySolution.enpoint.resolver.serviceEventTypeDisplayName', { + service: i18n.translate('xpack.securitySolution.endpoint.resolver.serviceEventTypeDisplayName', { defaultMessage: 'Service', }), - socket: i18n.translate('xpack.securitySolution.enpoint.resolver.socketEventTypeDisplayName', { + socket: i18n.translate('xpack.securitySolution.endpoint.resolver.socketEventTypeDisplayName', { defaultMessage: 'Socket', }), vulnerability: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.vulnerabilityEventTypeDisplayName', + 'xpack.securitySolution.endpoint.resolver.vulnerabilityEventTypeDisplayName', { defaultMessage: 'Vulnerability', } ), - web: i18n.translate('xpack.securitySolution.enpoint.resolver.webEventTypeDisplayName', { + web: i18n.translate('xpack.securitySolution.endpoint.resolver.webEventTypeDisplayName', { defaultMessage: 'Web', }), - alert: i18n.translate('xpack.securitySolution.enpoint.resolver.alertEventTypeDisplayName', { + alert: i18n.translate('xpack.securitySolution.endpoint.resolver.alertEventTypeDisplayName', { defaultMessage: 'Alert', }), - security: i18n.translate('xpack.securitySolution.enpoint.resolver.securityEventTypeDisplayName', { - defaultMessage: 'Security', - }), - dns: i18n.translate('xpack.securitySolution.enpoint.resolver.dnsEventTypeDisplayName', { + security: i18n.translate( + 'xpack.securitySolution.endpoint.resolver.securityEventTypeDisplayName', + { + defaultMessage: 'Security', + } + ), + dns: i18n.translate('xpack.securitySolution.endpoint.resolver.dnsEventTypeDisplayName', { defaultMessage: 'DNS', }), - clr: i18n.translate('xpack.securitySolution.enpoint.resolver.clrEventTypeDisplayName', { + clr: i18n.translate('xpack.securitySolution.endpoint.resolver.clrEventTypeDisplayName', { defaultMessage: 'CLR', }), image_load: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.image_loadEventTypeDisplayName', + 'xpack.securitySolution.endpoint.resolver.image_loadEventTypeDisplayName', { defaultMessage: 'Image Load', } ), powershell: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.powershellEventTypeDisplayName', + 'xpack.securitySolution.endpoint.resolver.powershellEventTypeDisplayName', { defaultMessage: 'Powershell', } ), - wmi: i18n.translate('xpack.securitySolution.enpoint.resolver.wmiEventTypeDisplayName', { + wmi: i18n.translate('xpack.securitySolution.endpoint.resolver.wmiEventTypeDisplayName', { defaultMessage: 'WMI', }), - api: i18n.translate('xpack.securitySolution.enpoint.resolver.apiEventTypeDisplayName', { + api: i18n.translate('xpack.securitySolution.endpoint.resolver.apiEventTypeDisplayName', { defaultMessage: 'API', }), - user: i18n.translate('xpack.securitySolution.enpoint.resolver.userEventTypeDisplayName', { + user: i18n.translate('xpack.securitySolution.endpoint.resolver.userEventTypeDisplayName', { defaultMessage: 'User', }), } as const; const unknownEventTypeMessage = i18n.translate( - 'xpack.securitySolution.enpoint.resolver.userEventTypeDisplayUnknown', + 'xpack.securitySolution.endpoint.resolver.userEventTypeDisplayUnknown', { defaultMessage: 'Unknown', } From 14db86c01f55e67a4188964c9a74ecbb6b550a71 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 11:24:05 -0400 Subject: [PATCH 75/78] R Austin review: spelling / i18n --- .../view/panels/panel_content_error.tsx | 6 +++--- .../panels/panel_content_process_detail.tsx | 20 +++++++++---------- .../panels/panel_content_process_list.tsx | 10 +++++----- .../panels/panel_content_related_counts.tsx | 8 ++++---- .../panels/panel_content_related_detail.tsx | 20 +++++++++---------- .../panels/panel_content_related_list.tsx | 14 ++++++------- .../view/panels/panel_content_utilities.tsx | 8 +++++++- 7 files changed, 46 insertions(+), 40 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx index df047cd82aa19..c9a536fd5932d 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx @@ -25,7 +25,7 @@ export const PanelContentError = memo(function ({ const crumbs = useMemo(() => { return [ { - text: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.error.events', { + text: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.error.events', { defaultMessage: 'Events', }), onClick: () => { @@ -33,7 +33,7 @@ export const PanelContentError = memo(function ({ }, }, { - text: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.error.error', { + text: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.error.error', { defaultMessage: 'Error', }), onClick: () => {}, @@ -51,7 +51,7 @@ export const PanelContentError = memo(function ({ pushToQueryParams({ crumbId: '', crumbEvent: '' }); }} > - {i18n.translate('xpack.securitySolution.enpoint.resolver.panel.error.goBack', { + {i18n.translate('xpack.securitySolution.endpoint.resolver.panel.error.goBack', { defaultMessage: 'Click this link to return to the list of all processes.', })} 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 8375e19c721ae..267187c56691a 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 @@ -53,7 +53,7 @@ export const ProcessDetails = memo(function ProcessDetails({ const createdEntry = { title: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.processDescList.created', + 'xpack.securitySolution.endpoint.resolver.panel.processDescList.created', { defaultMessage: 'Created', } @@ -62,21 +62,21 @@ export const ProcessDetails = memo(function ProcessDetails({ }; const pathEntry = { - title: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.path', { + title: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.processDescList.path', { defaultMessage: 'Path', }), description: processPath(processEvent), }; const pidEntry = { - title: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.pid', { + title: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.processDescList.pid', { defaultMessage: 'PID', }), description: processPid(processEvent), }; const userEntry = { - title: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.processDescList.user', { + title: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.processDescList.user', { defaultMessage: 'User', }), description: (userInfoForProcess(processEvent) as { name: string }).name, @@ -84,7 +84,7 @@ export const ProcessDetails = memo(function ProcessDetails({ const domainEntry = { title: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.processDescList.domain', + 'xpack.securitySolution.endpoint.resolver.panel.processDescList.domain', { defaultMessage: 'Domain', } @@ -94,7 +94,7 @@ export const ProcessDetails = memo(function ProcessDetails({ const parentPidEntry = { title: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.processDescList.parentPid', + 'xpack.securitySolution.endpoint.resolver.panel.processDescList.parentPid', { defaultMessage: 'Parent PID', } @@ -104,7 +104,7 @@ export const ProcessDetails = memo(function ProcessDetails({ const md5Entry = { title: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.processDescList.md5hash', + 'xpack.securitySolution.endpoint.resolver.panel.processDescList.md5hash', { defaultMessage: 'MD5', } @@ -114,7 +114,7 @@ export const ProcessDetails = memo(function ProcessDetails({ const commandLineEntry = { title: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.processDescList.commandLine', + 'xpack.securitySolution.endpoint.resolver.panel.processDescList.commandLine', { defaultMessage: 'Command Line', } @@ -150,7 +150,7 @@ export const ProcessDetails = memo(function ProcessDetails({ return [ { text: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.processDescList.events', + 'xpack.securitySolution.endpoint.resolver.panel.processDescList.events', { defaultMessage: 'Events', } @@ -163,7 +163,7 @@ export const ProcessDetails = memo(function ProcessDetails({ text: ( <> 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 bf23deac7c90a..86ae10b3b38c8 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 @@ -58,7 +58,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({ { field: 'name', name: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.table.row.processNameTitle', + 'xpack.securitySolution.endpoint.resolver.panel.table.row.processNameTitle', { defaultMessage: 'Process Name', } @@ -69,7 +69,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({ return name === '' ? ( {i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.table.row.valueMissingDescription', + 'xpack.securitySolution.endpoint.resolver.panel.table.row.valueMissingDescription', { defaultMessage: 'Value is missing', } @@ -91,7 +91,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({ { field: 'timestamp', name: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.table.row.timestampTitle', + 'xpack.securitySolution.endpoint.resolver.panel.table.row.timestampTitle', { defaultMessage: 'Timestamp', } @@ -104,7 +104,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({ ) : ( {i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.table.row.timestampInvalidLabel', + 'xpack.securitySolution.endpoint.resolver.panel.table.row.timestampInvalidLabel', { defaultMessage: 'invalid', } @@ -143,7 +143,7 @@ export const ProcessListWithCounts = memo(function ProcessListWithCounts({ return [ { text: i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.processListWithCounts.events', + 'xpack.securitySolution.endpoint.resolver.panel.processListWithCounts.events', { defaultMessage: 'All Process Events', } 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/panel_content_related_counts.tsx index 08ee89f070e4c..9d3b50516be99 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/panel_content_related_counts.tsx @@ -56,7 +56,7 @@ export const EventCountsForProcess = memo(function EventCountsForProcess({ 0 ); const eventsString = i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.processEventCounts.events', + 'xpack.securitySolution.endpoint.resolver.panel.processEventCounts.events', { defaultMessage: 'Events', } @@ -79,7 +79,7 @@ export const EventCountsForProcess = memo(function EventCountsForProcess({ text: ( <> @@ -105,7 +105,7 @@ export const EventCountsForProcess = memo(function EventCountsForProcess({ () => [ { field: 'count', - name: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.table.row.count', { + name: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.table.row.count', { defaultMessage: 'Count', }), width: '20%', @@ -113,7 +113,7 @@ export const EventCountsForProcess = memo(function EventCountsForProcess({ }, { field: 'name', - name: i18n.translate('xpack.securitySolution.enpoint.resolver.panel.table.row.eventType', { + name: i18n.translate('xpack.securitySolution.endpoint.resolver.panel.table.row.eventType', { defaultMessage: 'Event Type', }), width: '80%', 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/panel_content_related_detail.tsx index e5101f825b7c2..1fe6599e0829a 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/panel_content_related_detail.tsx @@ -103,13 +103,13 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ const processEntityId = parentEvent && event.entityId(parentEvent); const totalCount = countForParent || 0; const eventsString = i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.relatedEventDetail.events', + 'xpack.securitySolution.endpoint.resolver.panel.relatedEventDetail.events', { defaultMessage: 'Events', } ); const naString = i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.relatedEventDetail.NA', + 'xpack.securitySolution.endpoint.resolver.panel.relatedEventDetail.NA', { defaultMessage: 'N/A', } @@ -216,7 +216,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ text: ( <> @@ -230,7 +230,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ text: ( <> @@ -246,7 +246,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ { text: relatedEventToShowDetailsFor ? ( @@ -275,7 +275,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ */ if (!relatedsReady) { const waitingString = i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.relatedDetail.wait', + 'xpack.securitySolution.endpoint.resolver.panel.relatedDetail.wait', { defaultMessage: 'Waiting For Events...', } @@ -296,7 +296,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ */ if (!relatedEventToShowDetailsFor) { const errString = i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.relatedDetail.missing', + 'xpack.securitySolution.endpoint.resolver.panel.relatedDetail.missing', { defaultMessage: 'Related event not found.', } @@ -313,7 +313,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ @@ -330,7 +330,7 @@ export const RelatedEventDetail = memo(function RelatedEventDetail({ diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx index 07357e2ca1d72..c9c303010d10d 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_related_list.tsx @@ -53,7 +53,7 @@ const DisplayList = memo(function DisplayList({ @@ -70,7 +70,7 @@ const DisplayList = memo(function DisplayList({ @@ -102,13 +102,13 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr 0 ); const eventsString = i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.processEventListByType.events', + 'xpack.securitySolution.endpoint.resolver.panel.processEventListByType.events', { defaultMessage: 'Events', } ); const waitingString = i18n.translate( - 'xpack.securitySolution.enpoint.resolver.panel.processEventListByType.wait', + 'xpack.securitySolution.endpoint.resolver.panel.processEventListByType.wait', { defaultMessage: 'Waiting For Events...', } @@ -194,7 +194,7 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr text: ( <> @@ -208,7 +208,7 @@ export const ProcessEventListNarrowedByType = memo(function ProcessEventListNarr text: ( <> diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx index 781caf3fa44ca..65422d3d705d0 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_utilities.tsx @@ -70,6 +70,12 @@ export const formatter = new Intl.DateTimeFormat(i18n.getLocale(), { second: '2-digit', }); +const invalidDateText = i18n.translate( + 'xpack.securitySolution.enpdoint.resolver.panelutils.invaliddate', + { + defaultMessage: 'Invalid Date', + } +); /** * @param {ConstructorParameters[0]} timestamp To be passed through Date->Intl.DateTimeFormat * @returns {string} A nicely formatted string for a date @@ -79,6 +85,6 @@ export function formatDate(timestamp: ConstructorParameters[0]) { if (isFinite(date.getTime())) { return formatter.format(date); } else { - return 'Invalid Date'; + return invalidDateText; } } From 03476a964f37dedf500345d31d8b7eda5c77284e Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 12:25:32 -0400 Subject: [PATCH 76/78] CI: dup i18n title --- .../resolver/view/panels/panel_content_related_counts.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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/panel_content_related_counts.tsx index 9d3b50516be99..5900038ec5e5e 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/panel_content_related_counts.tsx @@ -79,7 +79,7 @@ export const EventCountsForProcess = memo(function EventCountsForProcess({ text: ( <> From 1c76b99f637466a69c45d5f7bba40dd55a50e131 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 13:30:02 -0400 Subject: [PATCH 77/78] CI: dup i18n title --- .../resolver/view/panels/panel_content_related_counts.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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/panel_content_related_counts.tsx index 5900038ec5e5e..2e4211f568ffe 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/panel_content_related_counts.tsx @@ -79,7 +79,7 @@ export const EventCountsForProcess = memo(function EventCountsForProcess({ text: ( <> From 1f05fddb304178d9fda8891a94975e2171c258a0 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 18 Jun 2020 14:40:42 -0400 Subject: [PATCH 78/78] CI: derped the name --- .../resolver/view/panels/panel_content_process_detail.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 267187c56691a..fcb7bf1d12e1b 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 @@ -163,7 +163,7 @@ export const ProcessDetails = memo(function ProcessDetails({ text: ( <>