From 6eace7e31e875aa66850fb70caf3300f82fc02f4 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 24 Feb 2020 16:47:12 -0500 Subject: [PATCH 01/16] adds base accordions to alert detail flyout --- .../endpoint/store/alerts/action.ts | 9 +- .../endpoint/store/alerts/middleware.ts | 11 +- .../endpoint/store/alerts/reducer.ts | 6 + .../endpoint/store/alerts/selectors.ts | 2 + .../public/applications/endpoint/types.ts | 3 + .../endpoint/view/alerts/alert_details.tsx | 311 ++++++++++++++++++ .../endpoint/view/alerts/index.tsx | 9 +- .../endpoint/view/alerts/resolver.tsx | 8 + .../plugins/endpoint/server/routes/alerts.ts | 150 +++++++++ 9 files changed, 502 insertions(+), 7 deletions(-) create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.tsx create mode 100644 x-pack/plugins/endpoint/server/routes/alerts.ts diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts index a628a95003a7f..6c6310a7349ed 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Immutable } from '../../../../../common/types'; +import { Immutable, AlertData } from '../../../../../common/types'; import { AlertListData } from '../../types'; interface ServerReturnedAlertsData { @@ -12,4 +12,9 @@ interface ServerReturnedAlertsData { readonly payload: Immutable; } -export type AlertAction = ServerReturnedAlertsData; +interface ServerReturnedAlertDetailsData { + readonly type: 'serverReturnedAlertDetailsData'; + readonly payload: Immutable; +} + +export type AlertAction = ServerReturnedAlertsData | ServerReturnedAlertDetailsData; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts index 76a6867418bd8..bd777f0eaaa20 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertResultList } from '../../../../../common/types'; +import { AlertResultList, AlertData } from '../../../../../common/types'; import { AppAction } from '../action'; import { MiddlewareFactory, AlertListState } from '../../types'; -import { isOnAlertPage, apiQueryParams } from './selectors'; +import { isOnAlertPage, apiQueryParams, hasSelectedAlert, uiQueryParams } from './selectors'; export const alertMiddlewareFactory: MiddlewareFactory = coreStart => { return api => next => async (action: AppAction) => { @@ -19,5 +19,12 @@ export const alertMiddlewareFactory: MiddlewareFactory = coreSta }); api.dispatch({ type: 'serverReturnedAlertsData', payload: response }); } + if (action.type === 'userChangedUrl' && isOnAlertPage(state) && hasSelectedAlert(state)) { + const { selected_alert } = uiQueryParams(state); + const response: AlertData = await coreStart.http.get( + `/api/endpoint/alerts/${selected_alert}` + ); + api.dispatch({ type: 'serverReturnedAlertDetailsData', payload: response }); + } }; }; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts index 77d7397d72581..969a7b653614a 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts @@ -11,6 +11,7 @@ import { AppAction } from '../action'; const initialState = (): AlertListState => { return { alerts: [], + alert_details: undefined, pageSize: 10, pageIndex: 0, total: 0, @@ -43,6 +44,11 @@ export const alertListReducer: Reducer = ( ...state, location: action.payload, }; + } else if (action.type === 'serverReturnedAlertDetailsData') { + return { + ...state, + alert_details: action.payload, + }; } return state; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts index f217e3cda9191..446d7884f3ba2 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts @@ -23,6 +23,8 @@ const createStructuredSelector: CreateStructuredSelector = createStructuredSelec */ export const alertListData = (state: AlertListState) => state.alerts; +export const selectedAlertDetailsData = (state: AlertListState) => state.alert_details; + /** * Returns the alert list pagination data from state */ diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts index 6498462a8fc06..1af5bd09c3ddb 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts @@ -106,6 +106,9 @@ export interface AlertListState { /** Current location object from React Router history. */ readonly location?: Immutable; + + /** Specific Alert data to be shown in the details view */ + readonly alert_details?: Immutable; } /** diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.tsx new file mode 100644 index 0000000000000..34492af3f1674 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.tsx @@ -0,0 +1,311 @@ +/* + * 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, { useMemo } from 'react'; +import { memo } from 'react'; +import { EuiAccordion, EuiSpacer, EuiDescriptionList } from '@elastic/eui'; +import { FormattedDate } from 'react-intl'; +import { useAlertListSelector } from './hooks/use_alerts_selector'; +import * as selectors from '../../store/alerts/selectors'; + +export const AlertDetails = memo(() => { + const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); + if (alertDetailsData === undefined) { + return null; + } + + const AlertDetailsDate = useMemo(() => { + const date = new Date(alertDetailsData['@timestamp']); + return ( + + ); + }, [alertDetailsData]); + + const alertDetailsColumns = useMemo(() => { + return [ + { + title: 'Alert Type', + description: 'TODO', + }, + { + title: 'Event Type', + description: alertDetailsData.event.action, + }, + { + title: 'Status', + description: 'TODO', + }, + { + title: 'Severity', + description: 'TODO', + }, + { + title: 'Date Created', + description: AlertDetailsDate, + }, + { + title: 'MalwareScore', + description: alertDetailsData.file_classification.malware_classification.score, + }, + { + title: 'File Name', + description: 'TODO', + }, + { + title: 'File Status', + description: 'TODO', + }, + { + title: 'File Type', + description: 'TODO', + }, + ]; + }, [AlertDetailsDate, alertDetailsData]); + + const hostDetailsColumns = useMemo(() => { + return [ + { + title: 'Host Name', + description: alertDetailsData.host.hostname, + }, + { + title: 'Host IP', + description: alertDetailsData.host.ip, + }, + { + title: 'Status', + description: 'TODO', + }, + { + title: 'OS', + description: alertDetailsData.host.os.name, + }, + { + title: 'Alert Count', + description: 'TODO', + }, + ]; + }, [alertDetailsData]); + + const hashDetailsColumns = useMemo(() => { + return [ + { + title: 'MD5', + description: 'TODO', + }, + { + title: 'SHA1', + description: 'TODO', + }, + { + title: 'SHA256', + description: 'TODO', + }, + ]; + }, []); + + const fileDetailsColumns = useMemo(() => { + return [ + { + title: 'File Name', + description: alertDetailsData.host.hostname, + }, + { + title: 'File Path', + description: alertDetailsData.host.ip, + }, + { + title: 'File Size', + description: 'TODO', + }, + { + title: 'File Created', + description: 'TODO', + }, + { + title: 'File Modified', + description: 'TODO', + }, + { + title: 'File Accessed', + description: 'TODO', + }, + { + title: 'Signer', + description: 'TODO', + }, + { + title: 'Owner', + description: 'TODO', + }, + ]; + }, [alertDetailsData]); + + const sourceProcessDetailsColumns = useMemo(() => { + return [ + { + title: 'Process ID', + description: 'TODO', + }, + { + title: 'Process Name', + description: 'TODO', + }, + { + title: 'Process Path', + description: 'TODO', + }, + { + title: 'MD5', + description: 'TODO', + }, + { + title: 'SHA1', + description: 'TODO', + }, + { + title: 'SHA256', + description: 'TODO', + }, + { + title: 'MalwareScore', + description: 'TODO', + }, + { + title: 'Parent Process ID', + description: 'TODO', + }, + { + title: 'Signer', + description: 'TODO', + }, + { + title: 'Username', + description: 'TODO', + }, + { + title: 'Domain', + description: 'TODO', + }, + ]; + }, []); + + const sourceProcessTokenDetailsColumns = useMemo(() => { + return [ + { + title: 'SID', + description: alertDetailsData.host.hostname, + }, + { + title: 'Integrity Level', + description: alertDetailsData.host.ip, + }, + { + title: 'Privileges', + description: 'TODO', + }, + ]; + }, [alertDetailsData]); + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx index 3c229484ede4e..d87a2063d26d4 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx @@ -17,6 +17,7 @@ import { EuiFlyoutBody, EuiTitle, EuiBadge, + EuiLoadingSpinner, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useHistory, Link } from 'react-router-dom'; @@ -25,7 +26,7 @@ import { urlFromQueryParams } from './url_from_query_params'; import { AlertData } from '../../../../../common/types'; import * as selectors from '../../store/alerts/selectors'; import { useAlertListSelector } from './hooks/use_alerts_selector'; -import { AlertDetailResolver } from './resolver'; +import { AlertDetails } from './alert_details'; export const AlertIndex = memo(() => { const history = useHistory(); @@ -138,7 +139,7 @@ export const AlertIndex = memo(() => { return ( {i18n.translate( 'xpack.endpoint.application.endpoint.alerts.alertType.maliciousFileDescription', @@ -201,6 +202,8 @@ export const AlertIndex = memo(() => { }; }, [onChangeItemsPerPage, onChangePage, pageIndex, pageSize]); + const selectedAlertData = useAlertListSelector(selectors.selectedAlertDetailsData); + return ( <> {hasSelectedAlert && ( @@ -215,7 +218,7 @@ export const AlertIndex = memo(() => { - + {selectedAlertData ? : } )} diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx index c7ef7f73dfe05..fb2b08d343daf 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx @@ -12,12 +12,20 @@ import { Resolver } from '../../../../embeddables/resolver/view'; import { EndpointPluginServices } from '../../../../plugin'; import { LegacyEndpointEvent } from '../../../../../common/types'; import { storeFactory } from '../../../../embeddables/resolver/store'; +import { useAlertListSelector } from './hooks/use_alerts_selector'; +import * as selectors from '../../store/alerts/selectors'; export const AlertDetailResolver = styled( React.memo( ({ className, selectedEvent }: { className?: string; selectedEvent?: LegacyEndpointEvent }) => { const context = useKibana(); const { store } = storeFactory(context); + // Different shit + const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); + if (alertDetailsData === undefined) { + return null; + } + return (
diff --git a/x-pack/plugins/endpoint/server/routes/alerts.ts b/x-pack/plugins/endpoint/server/routes/alerts.ts new file mode 100644 index 0000000000000..b62f8c9ce8c31 --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/alerts.ts @@ -0,0 +1,150 @@ +/* + * 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 { IRouter, KibanaRequest, RequestHandler } from 'kibana/server'; +import { SearchResponse } from 'elasticsearch'; +import { schema } from '@kbn/config-schema'; + +import { + getPagingProperties, + buildAlertListESQuery, +} from '../services/endpoint/alert_query_builders'; + +import { AlertData, AlertResultList } from '../../common/types'; +import { AlertRequestParams, EndpointAppContext, AlertDetailsRequestParams } from '../types'; + +const ALERTS_ROUTE = '/api/endpoint/alerts'; + +export const reqSchema = schema.object({ + page_size: schema.number({ defaultValue: 10, min: 1, max: 10000 }), + page_index: schema.number({ defaultValue: 0, min: 0 }), +}); + +export function registerAlertRoutes(router: IRouter, endpointAppContext: EndpointAppContext) { + const alertsHandler: RequestHandler = async (ctx, req, res) => { + try { + const queryParams = await getPagingProperties( + req as KibanaRequest, + endpointAppContext + ); + const reqBody = await buildAlertListESQuery(queryParams); + const response = (await ctx.core.elasticsearch.dataClient.callAsCurrentUser( + 'search', + reqBody + )) as SearchResponse; + return res.ok({ body: mapToAlertResultList(endpointAppContext, queryParams, response) }); + } catch (err) { + return res.internalError({ body: err }); + } + }; + + const alertDetailHandler: RequestHandler = async (ctx, req, res) => { + try { + function mapHit(entry: AlertDataWrapper): AlertData { + return { + id: entry._id, + ...entry._source, + }; + } + const alertId = req.params.id; + const response = (await ctx.core.elasticsearch.dataClient.callAsCurrentUser('get', { + index: 'my-index', + id: alertId, + })) as SearchResponse; + + return res.ok({ body: mapHit(response) }); + } catch (err) { + return res.internalError({ body: err }); + } + }; + + router.get( + { + path: ALERTS_ROUTE, + validate: { + query: reqSchema, + }, + options: { authRequired: true }, + }, + alertsHandler + ); + + router.post( + { + path: ALERTS_ROUTE, + validate: { + body: reqSchema, + }, + options: { authRequired: true }, + }, + alertsHandler + ); + + router.get( + { + path: ALERTS_ROUTE + '/{id}', + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + options: { authRequired: true }, + }, + alertDetailHandler + ); +} + +interface AlertDataWrapper { + _id: string; + _source: AlertData; +} + +function mapToAlertResultList( + endpointAppContext: EndpointAppContext, + queryParams: Record, + searchResponse: SearchResponse +): AlertResultList { + interface Total { + value: number; + relation: string; + } + + let totalNumberOfAlerts: number = 0; + let totalIsLowerBound: boolean = false; + + // We handle 2 separate schemas for the response below, due to: https://github.com/elastic/kibana/issues/56694 + if (typeof searchResponse?.hits?.total === 'object') { + const total: Total = searchResponse?.hits?.total as Total; + totalNumberOfAlerts = total?.value || 0; + totalIsLowerBound = total?.relation === 'gte' || false; + } else { + totalNumberOfAlerts = searchResponse?.hits?.total || 0; + } + + if (totalIsLowerBound) { + // This shouldn't happen, as we always try to fetch enough hits to satisfy the current request and the next page. + endpointAppContext.logFactory + .get('endpoint') + .warn('Total hits not counted accurately. Pagination numbers may be inaccurate.'); + } + + const hits: AlertDataWrapper[] = searchResponse?.hits?.hits; + + function mapHit(entry: AlertDataWrapper): AlertData { + return { + id: entry._id, + ...entry._source, + }; + } + + return { + request_page_size: queryParams.pageSize, + request_page_index: queryParams.pageIndex, + result_from_index: queryParams.fromIndex, + alerts: hits.map(mapHit), + total: totalNumberOfAlerts, + }; +} From a54cd587013b9a7f07b85f0e7db74629df3fede2 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 24 Feb 2020 17:47:57 -0500 Subject: [PATCH 02/16] adds hardcoded description blurb --- ...details.tsx => alert_details_overview.tsx} | 55 ++++++++++++++----- .../endpoint/view/alerts/index.tsx | 4 +- 2 files changed, 43 insertions(+), 16 deletions(-) rename x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/{alert_details.tsx => alert_details_overview.tsx} (82%) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx similarity index 82% rename from x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.tsx rename to x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx index 34492af3f1674..4687f5a2175e6 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx @@ -5,12 +5,19 @@ */ import React, { useMemo } from 'react'; import { memo } from 'react'; -import { EuiAccordion, EuiSpacer, EuiDescriptionList } from '@elastic/eui'; +import { + EuiAccordion, + EuiSpacer, + EuiDescriptionList, + EuiTitle, + EuiText, + EuiHealth, +} from '@elastic/eui'; import { FormattedDate } from 'react-intl'; import { useAlertListSelector } from './hooks/use_alerts_selector'; import * as selectors from '../../store/alerts/selectors'; -export const AlertDetails = memo(() => { +export const AlertDetailsOverview = memo(() => { const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); if (alertDetailsData === undefined) { return null; @@ -118,11 +125,11 @@ export const AlertDetails = memo(() => { return [ { title: 'File Name', - description: alertDetailsData.host.hostname, + description: 'TODO', }, { title: 'File Path', - description: alertDetailsData.host.ip, + description: 'TODO', }, { title: 'File Size', @@ -149,7 +156,7 @@ export const AlertDetails = memo(() => { description: 'TODO', }, ]; - }, [alertDetailsData]); + }, []); const sourceProcessDetailsColumns = useMemo(() => { return [ @@ -204,23 +211,43 @@ export const AlertDetails = memo(() => { return [ { title: 'SID', - description: alertDetailsData.host.hostname, + description: 'TODO', }, { title: 'Integrity Level', - description: alertDetailsData.host.ip, + description: 'TODO', }, { title: 'Privileges', description: 'TODO', }, ]; - }, [alertDetailsData]); + }, []); return ( <> + {/* Hard coded top level alert details component. TODO: maybe abstract somewhere else? */} + +

Detected Malicious File

+
+ + +

+ Endgame MalwareScore detected the opening of a document with a blah blah blah on{' '} + {alertDetailsData.host.hostname} on {AlertDetailsDate} +

+
+ + + Endpoint Status: Online + + + Alert Status: Open + + + {/* Start of Alert Details overview component TODO: delete this comment eventually */} { { { { { { const history = useHistory(); @@ -218,7 +218,7 @@ export const AlertIndex = memo(() => { - {selectedAlertData ? : } + {selectedAlertData ? : } )} From 0f091d5a8cdf3352f143c744090b171918c12c93 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Thu, 27 Feb 2020 13:29:22 -0500 Subject: [PATCH 03/16] adds more fields to alert data type --- x-pack/plugins/endpoint/common/types.ts | 88 +++++++++++++----- .../view/alerts/alert_details_overview.tsx | 92 ++++++++++++------- 2 files changed, 125 insertions(+), 55 deletions(-) diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index 6d904fda6f747..c7c3516742dff 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -96,6 +96,38 @@ export interface EndpointResultList { request_page_index: number; } +export interface OSFields { + full: string; + name: string; + version: string; + variant: string; +} + +export interface HostFields { + id: string; + hostname: string; + ip: string[]; + mac: string[]; + os: OSFields; +} + +export interface HashFields { + md5: string; + sha1: string; + sha256: string; + imphash?: string; +} + +export interface MalwareClassificationFields { + score: number; +} + +export interface PrivilegesFields { + description: string; + name: string; + enabled: boolean; +} + /** * Describes an Alert Event. * Should be in line with ECS schema. @@ -109,23 +141,42 @@ export type AlertEvent = Immutable<{ event: { id: string; action: string; + category: string; + kind: string; }; - file_classification: { - malware_classification: { - score: number; + process: { + pid: number; + ppid: number; + entity_id: string; + parent: { + entity_id: string; + }; + name: string; + hash: HashFields; + executable: string; + malware_classification: MalwareClassificationFields; + token: { + domain: string; + user: string; + sid: string; + integrity_level: number; + privileges: PrivilegesFields[]; }; }; - process?: { - unique_pid: number; - pid: number; + file_classification: { + malware_classification: MalwareClassificationFields; + signature_signer: string; }; - host: { - hostname: string; - ip: string; - os: { - name: string; - }; + file: { + owner: string; + path: string; + accessed: string; + mtime: string; + created: string; + size: number; + hash: HashFields; }; + host: HostFields; thread: {}; endpoint?: {}; endgame?: {}; @@ -161,18 +212,7 @@ export interface EndpointMetadata { id: string; name: string; }; - host: { - id: string; - hostname: string; - ip: string[]; - mac: string[]; - os: { - name: string; - full: string; - version: string; - variant: string; - }; - }; + host: HostFields; } /** diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx index 4687f5a2175e6..3c031676a8142 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx @@ -14,6 +14,7 @@ import { EuiHealth, } from '@elastic/eui'; import { FormattedDate } from 'react-intl'; +import { i18n } from '@kbn/i18n'; import { useAlertListSelector } from './hooks/use_alerts_selector'; import * as selectors from '../../store/alerts/selectors'; @@ -23,6 +24,7 @@ export const AlertDetailsOverview = memo(() => { return null; } + // TODO decide which version to keep const AlertDetailsDate = useMemo(() => { const date = new Date(alertDetailsData['@timestamp']); return ( @@ -38,15 +40,39 @@ export const AlertDetailsOverview = memo(() => { ); }, [alertDetailsData]); + // TODO fix this style + const TokenPrivileges = useMemo(() => { + const privileges: Array<{ title: string; description: string }> = []; + alertDetailsData.process.token.privileges.map(({ name, description }) => { + privileges.push({ title: name, description }); + }); + return ( + <> + + + + + ); + }, [alertDetailsData.process.token.privileges]); + + const dateFormatter = new Intl.DateTimeFormat(i18n.getLocale(), { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }); + const alertDetailsColumns = useMemo(() => { return [ { title: 'Alert Type', - description: 'TODO', + description: alertDetailsData.event.category, }, { title: 'Event Type', - description: alertDetailsData.event.action, + description: alertDetailsData.event.kind, }, { title: 'Status', @@ -58,7 +84,7 @@ export const AlertDetailsOverview = memo(() => { }, { title: 'Date Created', - description: AlertDetailsDate, + description: dateFormatter.format(new Date(alertDetailsData['@timestamp'])), }, { title: 'MalwareScore', @@ -77,7 +103,7 @@ export const AlertDetailsOverview = memo(() => { description: 'TODO', }, ]; - }, [AlertDetailsDate, alertDetailsData]); + }, [alertDetailsData, dateFormatter]); const hostDetailsColumns = useMemo(() => { return [ @@ -108,18 +134,18 @@ export const AlertDetailsOverview = memo(() => { return [ { title: 'MD5', - description: 'TODO', + description: alertDetailsData.file.hash.md5, }, { title: 'SHA1', - description: 'TODO', + description: alertDetailsData.file.hash.sha1, }, { title: 'SHA256', - description: 'TODO', + description: alertDetailsData.file.hash.sha256, }, ]; - }, []); + }, [alertDetailsData]); const fileDetailsColumns = useMemo(() => { return [ @@ -129,68 +155,68 @@ export const AlertDetailsOverview = memo(() => { }, { title: 'File Path', - description: 'TODO', + description: alertDetailsData.file.path, }, { title: 'File Size', - description: 'TODO', + description: alertDetailsData.file.size, }, { title: 'File Created', - description: 'TODO', + description: dateFormatter.format(new Date(alertDetailsData.file.created)), }, { title: 'File Modified', - description: 'TODO', + description: dateFormatter.format(new Date(alertDetailsData.file.mtime)), }, { title: 'File Accessed', - description: 'TODO', + description: dateFormatter.format(new Date(alertDetailsData.file.accessed)), }, { title: 'Signer', - description: 'TODO', + description: alertDetailsData.file_classification.signature_signer, }, { title: 'Owner', - description: 'TODO', + description: alertDetailsData.file.owner, }, ]; - }, []); + }, [alertDetailsData, dateFormatter]); const sourceProcessDetailsColumns = useMemo(() => { return [ { title: 'Process ID', - description: 'TODO', + description: alertDetailsData.process.pid, // TODO: Change me }, { title: 'Process Name', - description: 'TODO', + description: alertDetailsData.process.name, }, { title: 'Process Path', - description: 'TODO', + description: alertDetailsData.process.executable, }, { title: 'MD5', - description: 'TODO', + description: alertDetailsData.process.hash.md5, }, { title: 'SHA1', - description: 'TODO', + description: alertDetailsData.process.hash.sha1, }, { title: 'SHA256', - description: 'TODO', + description: alertDetailsData.process.hash.sha256, }, { title: 'MalwareScore', - description: 'TODO', + description: alertDetailsData.process.malware_classification.score, }, { title: 'Parent Process ID', - description: 'TODO', + description: alertDetailsData.process.ppid, // TODO: Change me }, { title: 'Signer', @@ -198,31 +224,35 @@ export const AlertDetailsOverview = memo(() => { }, { title: 'Username', - description: 'TODO', + description: alertDetailsData.process.token.user, // TODO: Not sure about this }, { title: 'Domain', - description: 'TODO', + description: alertDetailsData.process.token.domain, }, ]; - }, []); + }, [alertDetailsData]); const sourceProcessTokenDetailsColumns = useMemo(() => { return [ { title: 'SID', - description: 'TODO', + description: alertDetailsData.process.token.sid, }, { title: 'Integrity Level', - description: 'TODO', + description: alertDetailsData.process.token.integrity_level, }, { title: 'Privileges', - description: 'TODO', + description: TokenPrivileges, }, ]; - }, []); + }, [ + TokenPrivileges, + alertDetailsData.process.token.integrity_level, + alertDetailsData.process.token.sid, + ]); return ( <> From 1769e1d8bf6c7c927004bba1fbd6b1dc5128af77 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Thu, 27 Feb 2020 15:49:26 -0500 Subject: [PATCH 04/16] refactors names --- .../endpoint/store/alerts/middleware.ts | 4 +- .../endpoint/store/alerts/reducer.ts | 4 +- .../endpoint/store/alerts/selectors.ts | 2 +- .../public/applications/endpoint/types.ts | 10 +- .../view/alerts/alert_details_overview.tsx | 79 +++------ .../plugins/endpoint/server/routes/alerts.ts | 150 ------------------ 6 files changed, 29 insertions(+), 220 deletions(-) delete mode 100644 x-pack/plugins/endpoint/server/routes/alerts.ts diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts index bd777f0eaaa20..2cb381e901b4e 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts @@ -20,9 +20,9 @@ export const alertMiddlewareFactory: MiddlewareFactory = coreSta api.dispatch({ type: 'serverReturnedAlertsData', payload: response }); } if (action.type === 'userChangedUrl' && isOnAlertPage(state) && hasSelectedAlert(state)) { - const { selected_alert } = uiQueryParams(state); + const uiParams = uiQueryParams(state); const response: AlertData = await coreStart.http.get( - `/api/endpoint/alerts/${selected_alert}` + `/api/endpoint/alerts/${uiParams.selected_alert}` ); api.dispatch({ type: 'serverReturnedAlertDetailsData', payload: response }); } diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts index 969a7b653614a..ee172fa80f1fe 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts @@ -11,7 +11,7 @@ import { AppAction } from '../action'; const initialState = (): AlertListState => { return { alerts: [], - alert_details: undefined, + alertDetails: undefined, pageSize: 10, pageIndex: 0, total: 0, @@ -47,7 +47,7 @@ export const alertListReducer: Reducer = ( } else if (action.type === 'serverReturnedAlertDetailsData') { return { ...state, - alert_details: action.payload, + alertDetails: action.payload, }; } diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts index 446d7884f3ba2..876404bcffa48 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts @@ -23,7 +23,7 @@ const createStructuredSelector: CreateStructuredSelector = createStructuredSelec */ export const alertListData = (state: AlertListState) => state.alerts; -export const selectedAlertDetailsData = (state: AlertListState) => state.alert_details; +export const selectedAlertDetailsData = (state: AlertListState) => state.alertDetails; /** * Returns the alert list pagination data from state diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts index 1af5bd09c3ddb..b46785d3190e5 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts @@ -93,22 +93,22 @@ export type AlertListData = AlertResultList; export interface AlertListState { /** Array of alert items. */ - alerts: ImmutableArray; + readonly alerts: ImmutableArray; /** The total number of alerts on the page. */ - total: number; + readonly total: number; /** Number of alerts per page. */ - pageSize: number; + readonly pageSize: number; /** Page number, starting at 0. */ - pageIndex: number; + readonly pageIndex: number; /** Current location object from React Router history. */ readonly location?: Immutable; /** Specific Alert data to be shown in the details view */ - readonly alert_details?: Immutable; + readonly alertDetails?: Immutable; } /** diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx index 3c031676a8142..36d99080a2ed8 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx @@ -40,6 +40,15 @@ export const AlertDetailsOverview = memo(() => { ); }, [alertDetailsData]); + const dateFormatter = new Intl.DateTimeFormat(i18n.getLocale(), { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }); + // TODO fix this style const TokenPrivileges = useMemo(() => { const privileges: Array<{ title: string; description: string }> = []; @@ -49,21 +58,12 @@ export const AlertDetailsOverview = memo(() => { return ( <> - + ); }, [alertDetailsData.process.token.privileges]); - const dateFormatter = new Intl.DateTimeFormat(i18n.getLocale(), { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - }); - const alertDetailsColumns = useMemo(() => { return [ { @@ -282,56 +282,25 @@ export const AlertDetailsOverview = memo(() => { paddingSize="l" initialIsOpen={true} > - + - - + + - - + + - - + + @@ -340,13 +309,8 @@ export const AlertDetailsOverview = memo(() => { id="alertDetailsSourceProcessAccordion" buttonContent="Source Process" paddingSize="l" - initialIsOpen={true} > - + @@ -355,13 +319,8 @@ export const AlertDetailsOverview = memo(() => { id="alertDetailsSourceProcessTokenAccordion" buttonContent="Source Process Token" paddingSize="l" - initialIsOpen={true} > - + ); diff --git a/x-pack/plugins/endpoint/server/routes/alerts.ts b/x-pack/plugins/endpoint/server/routes/alerts.ts deleted file mode 100644 index b62f8c9ce8c31..0000000000000 --- a/x-pack/plugins/endpoint/server/routes/alerts.ts +++ /dev/null @@ -1,150 +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 { IRouter, KibanaRequest, RequestHandler } from 'kibana/server'; -import { SearchResponse } from 'elasticsearch'; -import { schema } from '@kbn/config-schema'; - -import { - getPagingProperties, - buildAlertListESQuery, -} from '../services/endpoint/alert_query_builders'; - -import { AlertData, AlertResultList } from '../../common/types'; -import { AlertRequestParams, EndpointAppContext, AlertDetailsRequestParams } from '../types'; - -const ALERTS_ROUTE = '/api/endpoint/alerts'; - -export const reqSchema = schema.object({ - page_size: schema.number({ defaultValue: 10, min: 1, max: 10000 }), - page_index: schema.number({ defaultValue: 0, min: 0 }), -}); - -export function registerAlertRoutes(router: IRouter, endpointAppContext: EndpointAppContext) { - const alertsHandler: RequestHandler = async (ctx, req, res) => { - try { - const queryParams = await getPagingProperties( - req as KibanaRequest, - endpointAppContext - ); - const reqBody = await buildAlertListESQuery(queryParams); - const response = (await ctx.core.elasticsearch.dataClient.callAsCurrentUser( - 'search', - reqBody - )) as SearchResponse; - return res.ok({ body: mapToAlertResultList(endpointAppContext, queryParams, response) }); - } catch (err) { - return res.internalError({ body: err }); - } - }; - - const alertDetailHandler: RequestHandler = async (ctx, req, res) => { - try { - function mapHit(entry: AlertDataWrapper): AlertData { - return { - id: entry._id, - ...entry._source, - }; - } - const alertId = req.params.id; - const response = (await ctx.core.elasticsearch.dataClient.callAsCurrentUser('get', { - index: 'my-index', - id: alertId, - })) as SearchResponse; - - return res.ok({ body: mapHit(response) }); - } catch (err) { - return res.internalError({ body: err }); - } - }; - - router.get( - { - path: ALERTS_ROUTE, - validate: { - query: reqSchema, - }, - options: { authRequired: true }, - }, - alertsHandler - ); - - router.post( - { - path: ALERTS_ROUTE, - validate: { - body: reqSchema, - }, - options: { authRequired: true }, - }, - alertsHandler - ); - - router.get( - { - path: ALERTS_ROUTE + '/{id}', - validate: { - params: schema.object({ - id: schema.string(), - }), - }, - options: { authRequired: true }, - }, - alertDetailHandler - ); -} - -interface AlertDataWrapper { - _id: string; - _source: AlertData; -} - -function mapToAlertResultList( - endpointAppContext: EndpointAppContext, - queryParams: Record, - searchResponse: SearchResponse -): AlertResultList { - interface Total { - value: number; - relation: string; - } - - let totalNumberOfAlerts: number = 0; - let totalIsLowerBound: boolean = false; - - // We handle 2 separate schemas for the response below, due to: https://github.com/elastic/kibana/issues/56694 - if (typeof searchResponse?.hits?.total === 'object') { - const total: Total = searchResponse?.hits?.total as Total; - totalNumberOfAlerts = total?.value || 0; - totalIsLowerBound = total?.relation === 'gte' || false; - } else { - totalNumberOfAlerts = searchResponse?.hits?.total || 0; - } - - if (totalIsLowerBound) { - // This shouldn't happen, as we always try to fetch enough hits to satisfy the current request and the next page. - endpointAppContext.logFactory - .get('endpoint') - .warn('Total hits not counted accurately. Pagination numbers may be inaccurate.'); - } - - const hits: AlertDataWrapper[] = searchResponse?.hits?.hits; - - function mapHit(entry: AlertDataWrapper): AlertData { - return { - id: entry._id, - ...entry._source, - }; - } - - return { - request_page_size: queryParams.pageSize, - request_page_index: queryParams.pageIndex, - result_from_index: queryParams.fromIndex, - alerts: hits.map(mapHit), - total: totalNumberOfAlerts, - }; -} From 0d86e09e9fe32d5f6b0566fbef7bf7a61868d699 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Thu, 27 Feb 2020 17:54:18 -0500 Subject: [PATCH 05/16] converts display text to i18n --- .../store/alerts/mock_alert_result_list.ts | 1 + .../view/alerts/alert_details_overview.tsx | 210 ++++++++++++++---- .../endpoint/view/alerts/index.tsx | 51 +++-- 3 files changed, 200 insertions(+), 62 deletions(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts index 8eadb3e7fb3df..3f0a1a4a094f1 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts @@ -26,6 +26,7 @@ export const mockAlertResultList: (options?: { const alerts = []; for (let index = 0; index < actualCountToReturn; index++) { alerts.push({ + // TODO: Update this data to the new type '@timestamp': 1542341895000, id: 'xDUYMHABAJk0XnHd8rrd', agent: { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx index 36d99080a2ed8..e494328052f6d 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx @@ -67,39 +67,57 @@ export const AlertDetailsOverview = memo(() => { const alertDetailsColumns = useMemo(() => { return [ { - title: 'Alert Type', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.alertType', { + defaultMessage: 'Alert Type', + }), description: alertDetailsData.event.category, }, { - title: 'Event Type', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.eventType', { + defaultMessage: 'Event Type', + }), description: alertDetailsData.event.kind, }, { - title: 'Status', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.status', { + defaultMessage: 'Status', + }), description: 'TODO', }, { - title: 'Severity', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.severity', { + defaultMessage: 'Severity', + }), description: 'TODO', }, { - title: 'Date Created', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.dateCreated', { + defaultMessage: 'Date Created', + }), description: dateFormatter.format(new Date(alertDetailsData['@timestamp'])), }, { - title: 'MalwareScore', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.malwareScore', { + defaultMessage: 'MalwareScore', + }), description: alertDetailsData.file_classification.malware_classification.score, }, { - title: 'File Name', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileName', { + defaultMessage: 'File Name', + }), description: 'TODO', }, { - title: 'File Status', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileStatus', { + defaultMessage: 'File Status', + }), description: 'TODO', }, { - title: 'File Type', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileType', { + defaultMessage: 'File Type', + }), description: 'TODO', }, ]; @@ -108,23 +126,33 @@ export const AlertDetailsOverview = memo(() => { const hostDetailsColumns = useMemo(() => { return [ { - title: 'Host Name', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.hostName', { + defaultMessage: 'Host Name', + }), description: alertDetailsData.host.hostname, }, { - title: 'Host IP', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.hostIP', { + defaultMessage: 'Host IP', + }), description: alertDetailsData.host.ip, }, { - title: 'Status', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.status', { + defaultMessage: 'Status', + }), description: 'TODO', }, { - title: 'OS', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.os', { + defaultMessage: 'OS', + }), description: alertDetailsData.host.os.name, }, { - title: 'Alert Count', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.alertCount', { + defaultMessage: 'Alert Count', + }), description: 'TODO', }, ]; @@ -133,15 +161,21 @@ export const AlertDetailsOverview = memo(() => { const hashDetailsColumns = useMemo(() => { return [ { - title: 'MD5', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.md5', { + defaultMessage: 'MD5', + }), description: alertDetailsData.file.hash.md5, }, { - title: 'SHA1', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sha1', { + defaultMessage: 'SHA1', + }), description: alertDetailsData.file.hash.sha1, }, { - title: 'SHA256', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sha256', { + defaultMessage: 'SHA256', + }), description: alertDetailsData.file.hash.sha256, }, ]; @@ -150,35 +184,51 @@ export const AlertDetailsOverview = memo(() => { const fileDetailsColumns = useMemo(() => { return [ { - title: 'File Name', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileName', { + defaultMessage: 'File Name', + }), description: 'TODO', }, { - title: 'File Path', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.filePath', { + defaultMessage: 'File Path', + }), description: alertDetailsData.file.path, }, { - title: 'File Size', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileSize', { + defaultMessage: 'File Size', + }), description: alertDetailsData.file.size, }, { - title: 'File Created', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileCreated', { + defaultMessage: 'File Created', + }), description: dateFormatter.format(new Date(alertDetailsData.file.created)), }, { - title: 'File Modified', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileModified', { + defaultMessage: 'File Modified', + }), description: dateFormatter.format(new Date(alertDetailsData.file.mtime)), }, { - title: 'File Accessed', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileAccessed', { + defaultMessage: 'File Accessed', + }), description: dateFormatter.format(new Date(alertDetailsData.file.accessed)), }, { - title: 'Signer', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.signer', { + defaultMessage: 'Signer', + }), description: alertDetailsData.file_classification.signature_signer, }, { - title: 'Owner', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.owner', { + defaultMessage: 'Owner', + }), description: alertDetailsData.file.owner, }, ]; @@ -187,47 +237,69 @@ export const AlertDetailsOverview = memo(() => { const sourceProcessDetailsColumns = useMemo(() => { return [ { - title: 'Process ID', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.processID', { + defaultMessage: 'Process ID', + }), description: alertDetailsData.process.pid, // TODO: Change me }, { - title: 'Process Name', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.processName', { + defaultMessage: 'Process Name', + }), description: alertDetailsData.process.name, }, { - title: 'Process Path', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.processPath', { + defaultMessage: 'Process Path', + }), description: alertDetailsData.process.executable, }, { - title: 'MD5', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.md5', { + defaultMessage: 'MD5', + }), description: alertDetailsData.process.hash.md5, }, { - title: 'SHA1', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sha1', { + defaultMessage: 'SHA1', + }), description: alertDetailsData.process.hash.sha1, }, { - title: 'SHA256', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sha256', { + defaultMessage: 'SHA256', + }), description: alertDetailsData.process.hash.sha256, }, { - title: 'MalwareScore', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.malwareScore', { + defaultMessage: 'MalwareScore', + }), description: alertDetailsData.process.malware_classification.score, }, { - title: 'Parent Process ID', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.parentProcessID', { + defaultMessage: 'Parent Process ID', + }), description: alertDetailsData.process.ppid, // TODO: Change me }, { - title: 'Signer', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.signer', { + defaultMessage: 'signer', + }), description: 'TODO', }, { - title: 'Username', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.username', { + defaultMessage: 'Username', + }), description: alertDetailsData.process.token.user, // TODO: Not sure about this }, { - title: 'Domain', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.domain', { + defaultMessage: 'Domain', + }), description: alertDetailsData.process.token.domain, }, ]; @@ -236,15 +308,21 @@ export const AlertDetailsOverview = memo(() => { const sourceProcessTokenDetailsColumns = useMemo(() => { return [ { - title: 'SID', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sid', { + defaultMessage: 'SID', + }), description: alertDetailsData.process.token.sid, }, { - title: 'Integrity Level', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.integrityLevel', { + defaultMessage: 'Integrity Level', + }), description: alertDetailsData.process.token.integrity_level, }, { - title: 'Privileges', + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.privileges', { + defaultMessage: 'Privileges', + }), description: TokenPrivileges, }, ]; @@ -278,7 +356,12 @@ export const AlertDetailsOverview = memo(() => { {/* Start of Alert Details overview component TODO: delete this comment eventually */} @@ -287,19 +370,46 @@ export const AlertDetailsOverview = memo(() => { - + - + - + @@ -307,7 +417,12 @@ export const AlertDetailsOverview = memo(() => { @@ -317,7 +432,12 @@ export const AlertDetailsOverview = memo(() => { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx index 86dc666d438a7..fd77c64c55ff5 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx @@ -18,10 +18,13 @@ import { EuiTitle, EuiBadge, EuiLoadingSpinner, + EuiPageContentHeader, + EuiPageContentHeaderSection, + EuiPageContentBody, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useHistory, Link } from 'react-router-dom'; -import { FormattedDate } from 'react-intl'; +import { FormattedDate, FormattedMessage } from 'react-intl'; import { urlFromQueryParams } from './url_from_query_params'; import { AlertData } from '../../../../../common/types'; import * as selectors from '../../store/alerts/selectors'; @@ -225,22 +228,36 @@ export const AlertIndex = memo(() => { - ({ - visibleColumns, - setVisibleColumns, - }), - [setVisibleColumns, visibleColumns] - )} - renderCellValue={renderCellValue} - pagination={pagination} - data-test-subj="alertListGrid" - data-testid="alertListGrid" - /> + + + +

+ +

+
+
+
+ + ({ + visibleColumns, + setVisibleColumns, + }), + [setVisibleColumns, visibleColumns] + )} + renderCellValue={renderCellValue} + pagination={pagination} + data-test-subj="alertListGrid" + data-testid="alertListGrid" + /> +
From 16a4953cb228bc65d634e25da6e0c72ea2c85020 Mon Sep 17 00:00:00 2001 From: Pedro Jaramillo Date: Fri, 28 Feb 2020 07:11:08 -0500 Subject: [PATCH 06/16] Reorganize files, add FormattedDate component --- x-pack/plugins/endpoint/common/types.ts | 7 +- .../view/alerts/alert_details_overview.tsx | 447 ------------------ .../endpoint/view/alerts/details/index.ts | 7 + .../details/metadata/file_accordion.tsx | 80 ++++ .../details/metadata/general_accordion.tsx | 86 ++++ .../details/metadata/hash_accordion.tsx | 49 ++ .../details/metadata/host_accordion.tsx | 61 +++ .../view/alerts/details/metadata/index.ts | 12 + .../metadata/source_process_accordion.tsx | 97 ++++ .../source_process_token_accordion.tsx | 51 ++ .../view/alerts/details/overview/index.tsx | 43 ++ .../details/overview/metadata_panel.tsx | 40 ++ .../endpoint/view/alerts/formatted_date.tsx | 22 + .../endpoint/view/alerts/index.tsx | 27 +- 14 files changed, 561 insertions(+), 468 deletions(-) delete mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/index.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/general_accordion.tsx create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/hash_accordion.tsx create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/index.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_accordion.tsx create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_token_accordion.tsx create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/metadata_panel.tsx create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/formatted_date.tsx diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index c7c3516742dff..c3f178a84a809 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -149,6 +149,7 @@ export type AlertEvent = Immutable<{ ppid: number; entity_id: string; parent: { + pid: number; entity_id: string; }; name: string; @@ -170,9 +171,9 @@ export type AlertEvent = Immutable<{ file: { owner: string; path: string; - accessed: string; - mtime: string; - created: string; + accessed: number; + mtime: number; + created: number; size: number; hash: HashFields; }; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx deleted file mode 100644 index e494328052f6d..0000000000000 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/alert_details_overview.tsx +++ /dev/null @@ -1,447 +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 React, { useMemo } from 'react'; -import { memo } from 'react'; -import { - EuiAccordion, - EuiSpacer, - EuiDescriptionList, - EuiTitle, - EuiText, - EuiHealth, -} from '@elastic/eui'; -import { FormattedDate } from 'react-intl'; -import { i18n } from '@kbn/i18n'; -import { useAlertListSelector } from './hooks/use_alerts_selector'; -import * as selectors from '../../store/alerts/selectors'; - -export const AlertDetailsOverview = memo(() => { - const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); - if (alertDetailsData === undefined) { - return null; - } - - // TODO decide which version to keep - const AlertDetailsDate = useMemo(() => { - const date = new Date(alertDetailsData['@timestamp']); - return ( - - ); - }, [alertDetailsData]); - - const dateFormatter = new Intl.DateTimeFormat(i18n.getLocale(), { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - }); - - // TODO fix this style - const TokenPrivileges = useMemo(() => { - const privileges: Array<{ title: string; description: string }> = []; - alertDetailsData.process.token.privileges.map(({ name, description }) => { - privileges.push({ title: name, description }); - }); - return ( - <> - - - - - ); - }, [alertDetailsData.process.token.privileges]); - - const alertDetailsColumns = useMemo(() => { - return [ - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.alertType', { - defaultMessage: 'Alert Type', - }), - description: alertDetailsData.event.category, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.eventType', { - defaultMessage: 'Event Type', - }), - description: alertDetailsData.event.kind, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.status', { - defaultMessage: 'Status', - }), - description: 'TODO', - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.severity', { - defaultMessage: 'Severity', - }), - description: 'TODO', - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.dateCreated', { - defaultMessage: 'Date Created', - }), - description: dateFormatter.format(new Date(alertDetailsData['@timestamp'])), - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.malwareScore', { - defaultMessage: 'MalwareScore', - }), - description: alertDetailsData.file_classification.malware_classification.score, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileName', { - defaultMessage: 'File Name', - }), - description: 'TODO', - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileStatus', { - defaultMessage: 'File Status', - }), - description: 'TODO', - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileType', { - defaultMessage: 'File Type', - }), - description: 'TODO', - }, - ]; - }, [alertDetailsData, dateFormatter]); - - const hostDetailsColumns = useMemo(() => { - return [ - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.hostName', { - defaultMessage: 'Host Name', - }), - description: alertDetailsData.host.hostname, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.hostIP', { - defaultMessage: 'Host IP', - }), - description: alertDetailsData.host.ip, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.status', { - defaultMessage: 'Status', - }), - description: 'TODO', - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.os', { - defaultMessage: 'OS', - }), - description: alertDetailsData.host.os.name, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.alertCount', { - defaultMessage: 'Alert Count', - }), - description: 'TODO', - }, - ]; - }, [alertDetailsData]); - - const hashDetailsColumns = useMemo(() => { - return [ - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.md5', { - defaultMessage: 'MD5', - }), - description: alertDetailsData.file.hash.md5, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sha1', { - defaultMessage: 'SHA1', - }), - description: alertDetailsData.file.hash.sha1, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sha256', { - defaultMessage: 'SHA256', - }), - description: alertDetailsData.file.hash.sha256, - }, - ]; - }, [alertDetailsData]); - - const fileDetailsColumns = useMemo(() => { - return [ - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileName', { - defaultMessage: 'File Name', - }), - description: 'TODO', - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.filePath', { - defaultMessage: 'File Path', - }), - description: alertDetailsData.file.path, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileSize', { - defaultMessage: 'File Size', - }), - description: alertDetailsData.file.size, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileCreated', { - defaultMessage: 'File Created', - }), - description: dateFormatter.format(new Date(alertDetailsData.file.created)), - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileModified', { - defaultMessage: 'File Modified', - }), - description: dateFormatter.format(new Date(alertDetailsData.file.mtime)), - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileAccessed', { - defaultMessage: 'File Accessed', - }), - description: dateFormatter.format(new Date(alertDetailsData.file.accessed)), - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.signer', { - defaultMessage: 'Signer', - }), - description: alertDetailsData.file_classification.signature_signer, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.owner', { - defaultMessage: 'Owner', - }), - description: alertDetailsData.file.owner, - }, - ]; - }, [alertDetailsData, dateFormatter]); - - const sourceProcessDetailsColumns = useMemo(() => { - return [ - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.processID', { - defaultMessage: 'Process ID', - }), - description: alertDetailsData.process.pid, // TODO: Change me - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.processName', { - defaultMessage: 'Process Name', - }), - description: alertDetailsData.process.name, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.processPath', { - defaultMessage: 'Process Path', - }), - description: alertDetailsData.process.executable, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.md5', { - defaultMessage: 'MD5', - }), - description: alertDetailsData.process.hash.md5, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sha1', { - defaultMessage: 'SHA1', - }), - description: alertDetailsData.process.hash.sha1, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sha256', { - defaultMessage: 'SHA256', - }), - description: alertDetailsData.process.hash.sha256, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.malwareScore', { - defaultMessage: 'MalwareScore', - }), - description: alertDetailsData.process.malware_classification.score, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.parentProcessID', { - defaultMessage: 'Parent Process ID', - }), - description: alertDetailsData.process.ppid, // TODO: Change me - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.signer', { - defaultMessage: 'signer', - }), - description: 'TODO', - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.username', { - defaultMessage: 'Username', - }), - description: alertDetailsData.process.token.user, // TODO: Not sure about this - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.domain', { - defaultMessage: 'Domain', - }), - description: alertDetailsData.process.token.domain, - }, - ]; - }, [alertDetailsData]); - - const sourceProcessTokenDetailsColumns = useMemo(() => { - return [ - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sid', { - defaultMessage: 'SID', - }), - description: alertDetailsData.process.token.sid, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.integrityLevel', { - defaultMessage: 'Integrity Level', - }), - description: alertDetailsData.process.token.integrity_level, - }, - { - title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.privileges', { - defaultMessage: 'Privileges', - }), - description: TokenPrivileges, - }, - ]; - }, [ - TokenPrivileges, - alertDetailsData.process.token.integrity_level, - alertDetailsData.process.token.sid, - ]); - - return ( - <> - {/* Hard coded top level alert details component. TODO: maybe abstract somewhere else? */} - -

Detected Malicious File

-
- - -

- Endgame MalwareScore detected the opening of a document with a blah blah blah on{' '} - {alertDetailsData.host.hostname} on {AlertDetailsDate} -

-
- - - Endpoint Status: Online - - - Alert Status: Open - - - {/* Start of Alert Details overview component TODO: delete this comment eventually */} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/index.ts new file mode 100644 index 0000000000000..1c78309474737 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/index.ts @@ -0,0 +1,7 @@ +/* + * 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. + */ + +export { AlertDetailsOverview } from './overview'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx new file mode 100644 index 0000000000000..ac9240cd78470 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx @@ -0,0 +1,80 @@ +/* + * 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 { EuiAccordion, EuiDescriptionList } from '@elastic/eui'; +import { Immutable, AlertData } from '../../../../../../../common/types'; +import { FormattedDate } from '../../formatted_date'; + +export const FileAccordion = memo(({ alertData }: { alertData: Immutable }) => { + const columns = useMemo(() => { + return [ + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileName', { + defaultMessage: 'File Name', + }), + description: 'TODO', + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.filePath', { + defaultMessage: 'File Path', + }), + description: alertData.file.path, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileSize', { + defaultMessage: 'File Size', + }), + description: alertData.file.size, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileCreated', { + defaultMessage: 'File Created', + }), + description: , + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileModified', { + defaultMessage: 'File Modified', + }), + description: , + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileAccessed', { + defaultMessage: 'File Accessed', + }), + description: , + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.signer', { + defaultMessage: 'Signer', + }), + description: alertData.file_classification.signature_signer, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.owner', { + defaultMessage: 'Owner', + }), + description: alertData.file.owner, + }, + ]; + }, [alertData]); + + return ( + + + + ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/general_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/general_accordion.tsx new file mode 100644 index 0000000000000..02988142e0055 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/general_accordion.tsx @@ -0,0 +1,86 @@ +/* + * 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 { EuiAccordion, EuiDescriptionList } from '@elastic/eui'; +import { Immutable, AlertData } from '../../../../../../../common/types'; +import { FormattedDate } from '../../formatted_date'; + +export const GeneralAccordion = memo(({ alertData }: { alertData: Immutable }) => { + const columns = useMemo(() => { + return [ + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.alertType', { + defaultMessage: 'Alert Type', + }), + description: alertData.event.category, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.eventType', { + defaultMessage: 'Event Type', + }), + description: alertData.event.kind, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.status', { + defaultMessage: 'Status', + }), + description: 'TODO', + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.severity', { + defaultMessage: 'Severity', + }), + description: 'TODO', + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.dateCreated', { + defaultMessage: 'Date Created', + }), + description: , + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.malwareScore', { + defaultMessage: 'MalwareScore', + }), + description: alertData.file_classification.malware_classification.score, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileName', { + defaultMessage: 'File Name', + }), + description: 'TODO', + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileStatus', { + defaultMessage: 'File Status', + }), + description: 'TODO', + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.fileType', { + defaultMessage: 'File Type', + }), + description: 'TODO', + }, + ]; + }, [alertData]); + return ( + + + + ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/hash_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/hash_accordion.tsx new file mode 100644 index 0000000000000..b2be083ce8f59 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/hash_accordion.tsx @@ -0,0 +1,49 @@ +/* + * 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 { EuiAccordion, EuiDescriptionList } from '@elastic/eui'; +import { Immutable, AlertData } from '../../../../../../../common/types'; + +export const HashAccordion = memo(({ alertData }: { alertData: Immutable }) => { + const columns = useMemo(() => { + return [ + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.md5', { + defaultMessage: 'MD5', + }), + description: alertData.file.hash.md5, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sha1', { + defaultMessage: 'SHA1', + }), + description: alertData.file.hash.sha1, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sha256', { + defaultMessage: 'SHA256', + }), + description: alertData.file.hash.sha256, + }, + ]; + }, [alertData]); + + return ( + + + + ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx new file mode 100644 index 0000000000000..50f0ab324dbb4 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.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 React, { memo, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiAccordion, EuiDescriptionList } from '@elastic/eui'; +import { Immutable, AlertData } from '../../../../../../../common/types'; + +export const HostAccordion = memo(({ alertData }: { alertData: Immutable }) => { + const columns = useMemo(() => { + return [ + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.hostName', { + defaultMessage: 'Host Name', + }), + description: alertData.host.hostname, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.hostIP', { + defaultMessage: 'Host IP', + }), + description: alertData.host.ip, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.status', { + defaultMessage: 'Status', + }), + description: 'TODO', + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.os', { + defaultMessage: 'OS', + }), + description: alertData.host.os.name, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.alertCount', { + defaultMessage: 'Alert Count', + }), + description: 'TODO', + }, + ]; + }, [alertData]); + + return ( + + + + ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/index.ts new file mode 100644 index 0000000000000..1eb755242d701 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/index.ts @@ -0,0 +1,12 @@ +/* + * 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. + */ + +export { GeneralAccordion } from './general_accordion'; +export { HostAccordion } from './host_accordion'; +export { HashAccordion } from './hash_accordion'; +export { FileAccordion } from './file_accordion'; +export { SourceProcessAccordion } from './source_process_accordion'; +export { SourceProcessTokenAccordion } from './source_process_token_accordion'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_accordion.tsx new file mode 100644 index 0000000000000..4fbad4c73fd51 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_accordion.tsx @@ -0,0 +1,97 @@ +/* + * 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 { EuiAccordion, EuiDescriptionList } from '@elastic/eui'; +import { Immutable, AlertData } from '../../../../../../../common/types'; + +export const SourceProcessAccordion = memo(({ alertData }: { alertData: Immutable }) => { + const columns = useMemo(() => { + return [ + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.processID', { + defaultMessage: 'Process ID', + }), + description: alertData.process.pid, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.processName', { + defaultMessage: 'Process Name', + }), + description: alertData.process.name, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.processPath', { + defaultMessage: 'Process Path', + }), + description: alertData.process.executable, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.md5', { + defaultMessage: 'MD5', + }), + description: alertData.process.hash.md5, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sha1', { + defaultMessage: 'SHA1', + }), + description: alertData.process.hash.sha1, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sha256', { + defaultMessage: 'SHA256', + }), + description: alertData.process.hash.sha256, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.malwareScore', { + defaultMessage: 'MalwareScore', + }), + description: alertData.process.malware_classification.score, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.parentProcessID', { + defaultMessage: 'Parent Process ID', + }), + description: alertData.process.parent.pid, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.signer', { + defaultMessage: 'signer', + }), + description: 'TODO', + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.username', { + defaultMessage: 'Username', + }), + description: alertData.process.token.user, // TODO: Not sure about this + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.domain', { + defaultMessage: 'Domain', + }), + description: alertData.process.token.domain, + }, + ]; + }, [alertData]); + + return ( + + + + ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_token_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_token_accordion.tsx new file mode 100644 index 0000000000000..9ef2566481307 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_token_accordion.tsx @@ -0,0 +1,51 @@ +/* + * 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 { EuiAccordion, EuiDescriptionList } from '@elastic/eui'; +import { Immutable, AlertData } from '../../../../../../../common/types'; + +export const SourceProcessTokenAccordion = memo( + ({ alertData }: { alertData: Immutable }) => { + const columns = useMemo(() => { + return [ + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.sid', { + defaultMessage: 'SID', + }), + description: alertData.process.token.sid, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.integrityLevel', { + defaultMessage: 'Integrity Level', + }), + description: alertData.process.token.integrity_level, + }, + { + title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.privileges', { + defaultMessage: 'Privileges', + }), + description: 'TODO', + }, + ]; + }, [alertData]); + + return ( + + + + ); + } +); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx new file mode 100644 index 0000000000000..3c271e24679cc --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { memo } from 'react'; +import { EuiSpacer, EuiTitle, EuiText, EuiHealth } from '@elastic/eui'; +import { useAlertListSelector } from '../../hooks/use_alerts_selector'; +import * as selectors from '../../../../store/alerts/selectors'; +import { MetadataPanel } from './metadata_panel'; +import { FormattedDate } from '../../formatted_date'; + +export const AlertDetailsOverview = memo(() => { + const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); + if (alertDetailsData === undefined) { + return null; + } + + return ( + <> +
+ +

Detected Malicious File

+
+ + +

+ Endgame MalwareScore detected the opening of a document with a blah blah blah on{' '} + {alertDetailsData.host.hostname} on{' '} + {} +

+
+ + + Endpoint Status: Online + + Alert Status: Open + +
+ + + ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/metadata_panel.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/metadata_panel.tsx new file mode 100644 index 0000000000000..556d7bea2e310 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/metadata_panel.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { memo } from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import { useAlertListSelector } from '../../hooks/use_alerts_selector'; +import * as selectors from '../../../../store/alerts/selectors'; +import { + GeneralAccordion, + HostAccordion, + HashAccordion, + FileAccordion, + SourceProcessAccordion, + SourceProcessTokenAccordion, +} from '../metadata'; + +export const MetadataPanel = memo(() => { + const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); + if (alertDetailsData === undefined) { + return null; + } + return ( +
+ + + + + + + + + + + +
+ ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/formatted_date.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/formatted_date.tsx new file mode 100644 index 0000000000000..731bd31b26cef --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/formatted_date.tsx @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { memo } from 'react'; +import { FormattedDate as ReactIntlFormattedDate } from '@kbn/i18n/react'; + +export const FormattedDate = memo(({ timestamp }: { timestamp: number }) => { + const date = new Date(timestamp); + return ( + + ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx index fd77c64c55ff5..c914edc4d9298 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx @@ -24,12 +24,13 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useHistory, Link } from 'react-router-dom'; -import { FormattedDate, FormattedMessage } from 'react-intl'; +import { FormattedMessage } from '@kbn/i18n/react'; import { urlFromQueryParams } from './url_from_query_params'; import { AlertData } from '../../../../../common/types'; import * as selectors from '../../store/alerts/selectors'; import { useAlertListSelector } from './hooks/use_alerts_selector'; -import { AlertDetailsOverview } from './alert_details_overview'; +import { AlertDetailsOverview } from './details'; +import { FormattedDate } from './formatted_date'; export const AlertIndex = memo(() => { const history = useHistory(); @@ -123,10 +124,10 @@ export const AlertIndex = memo(() => { history.push(urlFromQueryParams(paramsWithoutSelectedAlert)); }, [history, queryParams]); - const datesForRows: Map = useMemo(() => { + const timestampForRows: Map = useMemo(() => { return new Map( alertListData.map(alertData => { - return [alertData, new Date(alertData['@timestamp'])]; + return [alertData, alertData['@timestamp']]; }) ); }, [alertListData]); @@ -161,19 +162,9 @@ export const AlertIndex = memo(() => { } else if (columnId === 'host_name') { return row.host.hostname; } else if (columnId === 'timestamp') { - const date = datesForRows.get(row)!; - if (date && isFinite(date.getTime())) { - return ( - - ); + const timestamp = timestampForRows.get(row)!; + if (timestamp) { + return ; } else { return ( @@ -193,7 +184,7 @@ export const AlertIndex = memo(() => { } return null; }; - }, [alertListData, datesForRows, pageSize, queryParams, total]); + }, [alertListData, timestampForRows, pageSize, queryParams, total]); const pagination = useMemo(() => { return { From 9e82edd79b0356d39863ce5e4c5e11122ca37129 Mon Sep 17 00:00:00 2001 From: Pedro Jaramillo Date: Fri, 28 Feb 2020 09:20:44 -0500 Subject: [PATCH 07/16] Add tabs --- .../view/alerts/details/overview/index.tsx | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx index 3c271e24679cc..4d6d39271cbb2 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx @@ -3,8 +3,9 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { memo } from 'react'; -import { EuiSpacer, EuiTitle, EuiText, EuiHealth } from '@elastic/eui'; +import React, { memo, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiSpacer, EuiTitle, EuiText, EuiHealth, EuiTabbedContent } from '@elastic/eui'; import { useAlertListSelector } from '../../hooks/use_alerts_selector'; import * as selectors from '../../../../store/alerts/selectors'; import { MetadataPanel } from './metadata_panel'; @@ -16,6 +17,36 @@ export const AlertDetailsOverview = memo(() => { return null; } + const tabs = useMemo(() => { + return [ + { + id: 'overviewMetadata', + name: i18n.translate( + 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.overview', + { + defaultMessage: 'Overview', + } + ), + content: ( + <> + + + + ), + }, + { + id: 'overviewResolver', + name: i18n.translate( + 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.resolver', + { + defaultMessage: 'Resolver', + } + ), + content: 'Resolver', + }, + ]; + }, []); + return ( <>
@@ -37,7 +68,7 @@ export const AlertDetailsOverview = memo(() => { Alert Status: Open
- + ); }); From 6452fb6fe18a4ee2d60a13203c0e06806ce3124a Mon Sep 17 00:00:00 2001 From: Pedro Jaramillo Date: Fri, 28 Feb 2020 11:04:00 -0500 Subject: [PATCH 08/16] Remove fields, intl some text --- x-pack/plugins/endpoint/common/types.ts | 1 + .../details/metadata/file_accordion.tsx | 2 +- .../details/metadata/general_accordion.tsx | 14 +------------- .../details/metadata/host_accordion.tsx | 6 ------ .../view/alerts/details/overview/index.tsx | 19 +++++++++++++++---- 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index c3f178a84a809..1662536bf6cd3 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -170,6 +170,7 @@ export type AlertEvent = Immutable<{ }; file: { owner: string; + name: string; path: string; accessed: number; mtime: number; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx index ac9240cd78470..b5c7f376df730 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx @@ -16,7 +16,7 @@ export const FileAccordion = memo(({ alertData }: { alertData: Immutable { <>
-

Detected Malicious File

+

+ +

- Endgame MalwareScore detected the opening of a document with a blah blah blah on{' '} - {alertDetailsData.host.hostname} on{' '} - {} + , + }} + />

From 67c8f1175d5d86f706de479e30e1c58da814c8d7 Mon Sep 17 00:00:00 2001 From: Pedro Jaramillo Date: Fri, 28 Feb 2020 11:13:17 -0500 Subject: [PATCH 09/16] remove severity --- .../view/alerts/details/metadata/general_accordion.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/general_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/general_accordion.tsx index fe228ab168599..1bfec054bad11 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/general_accordion.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/general_accordion.tsx @@ -30,12 +30,6 @@ export const GeneralAccordion = memo(({ alertData }: { alertData: Immutable Date: Fri, 28 Feb 2020 14:30:09 -0500 Subject: [PATCH 10/16] update fields based on new schema --- x-pack/plugins/endpoint/common/types.ts | 78 +++++++++++++++---- .../endpoint/store/alerts/selectors.ts | 14 ---- .../details/metadata/file_accordion.tsx | 2 +- .../details/metadata/general_accordion.tsx | 2 +- .../metadata/source_process_accordion.tsx | 8 +- .../source_process_token_accordion.tsx | 6 -- .../endpoint/view/alerts/index.tsx | 2 +- 7 files changed, 72 insertions(+), 40 deletions(-) diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index 1662536bf6cd3..d549b96080a5e 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -102,7 +102,6 @@ export interface OSFields { version: string; variant: string; } - export interface HostFields { id: string; hostname: string; @@ -110,23 +109,45 @@ export interface HostFields { mac: string[]; os: OSFields; } - export interface HashFields { md5: string; sha1: string; sha256: string; - imphash?: string; } - -export interface MalwareClassificationFields { +export interface MalwareClassifierFields { + identifier: string; score: number; + threshold: number; + version: string; } - export interface PrivilegesFields { description: string; name: string; enabled: boolean; } +export interface ThreadFields { + id: number; + service_name: string; + start: number; + start_address: number; + start_address_module: string; +} +export interface DllFields { + pe: { + architecture: string; + imphash: string; + }; + code_signature: { + subject_name: string; + trusted: boolean; + }; + compile_time: number; + hash: HashFields; + malware_classifier: MalwareClassifierFields; + mapped_address: number; + mapped_size: number; + path: string; +} /** * Describes an Alert Event. @@ -135,7 +156,10 @@ export interface PrivilegesFields { export type AlertEvent = Immutable<{ '@timestamp': number; agent: { + ephemeral_id: string; id: string; + name: string; + type: string; version: string; }; event: { @@ -143,8 +167,17 @@ export type AlertEvent = Immutable<{ action: string; category: string; kind: string; + dataset: string; + module: string; + type: string; }; process: { + code_signature: { + subject_name: string; + trusted: boolean; + }; + command_line: string; + domain: string; pid: number; ppid: number; entity_id: string; @@ -154,19 +187,22 @@ export type AlertEvent = Immutable<{ }; name: string; hash: HashFields; + pe: { + imphash: string; + }; executable: string; - malware_classification: MalwareClassificationFields; + sid: string; + start: number; + malware_classifier: MalwareClassifierFields; token: { domain: string; + type: string; user: string; sid: string; integrity_level: number; privileges: PrivilegesFields[]; }; - }; - file_classification: { - malware_classification: MalwareClassificationFields; - signature_signer: string; + thread: ThreadFields[]; }; file: { owner: string; @@ -177,11 +213,27 @@ export type AlertEvent = Immutable<{ created: number; size: number; hash: HashFields; + pe: { + imphash: string; + }; + code_signature: { + trusted: boolean; + subject_name: string; + }; + malware_classifier: { + features: { + data: { + buffer: string; + decompressed_size: number; + encoding: string; + }; + }; + } & MalwareClassifierFields; + temp_file_path: string; }; host: HostFields; thread: {}; - endpoint?: {}; - endgame?: {}; + dll: DllFields[]; }>; /** diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts index 876404bcffa48..d81bf3e34c85b 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts @@ -101,17 +101,3 @@ export const hasSelectedAlert: (state: AlertListState) => boolean = createSelect function isAlertEventLegacyEndpointEvent(event: { endgame?: {} }): event is LegacyEndpointEvent { return event.endgame !== undefined && 'unique_pid' in event.endgame; } - -export const selectedEvent: ( - state: AlertListState -) => LegacyEndpointEvent | undefined = createSelector( - uiQueryParams, - alertListData, - ({ selected_alert: selectedAlert }, alertList) => { - const found = alertList.find(alert => alert.event.id === selectedAlert); - if (!found) { - return found; - } - return isAlertEventLegacyEndpointEvent(found) ? found : undefined; - } -); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx index b5c7f376df730..ac67e54f38779 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/file_accordion.tsx @@ -52,7 +52,7 @@ export const FileAccordion = memo(({ alertData }: { alertData: Immutable { } else if (columnId === 'archived') { return null; } else if (columnId === 'malware_score') { - return row.file_classification.malware_classification.score; + return row.file.malware_classifier.score; } return null; }; From fddef5ddb40da4a3a857c5303dc84ea960a79f43 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Fri, 28 Feb 2020 16:34:15 -0500 Subject: [PATCH 11/16] adding tests --- x-pack/plugins/endpoint/common/types.ts | 6 +- .../store/alerts/alert_details.test.ts | 57 +++++++ .../store/alerts/mock_alert_result_list.ts | 154 ++++++++++++++++-- .../details/metadata/host_accordion.tsx | 2 +- 4 files changed, 199 insertions(+), 20 deletions(-) create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_details.test.ts diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index d549b96080a5e..d804350a9002d 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -156,10 +156,7 @@ export interface DllFields { export type AlertEvent = Immutable<{ '@timestamp': number; agent: { - ephemeral_id: string; id: string; - name: string; - type: string; version: string; }; event: { @@ -200,9 +197,12 @@ export type AlertEvent = Immutable<{ user: string; sid: string; integrity_level: number; + integrity_level_name: string; privileges: PrivilegesFields[]; }; thread: ThreadFields[]; + uptime: number; + user: string; }; file: { owner: string; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_details.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_details.test.ts new file mode 100644 index 0000000000000..cf9b30ae99179 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_details.test.ts @@ -0,0 +1,57 @@ +/* + * 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 { Store, createStore, applyMiddleware } from 'redux'; +import { History } from 'history'; +import { alertListReducer } from './reducer'; +import { AlertListState } from '../../types'; +import { alertMiddlewareFactory } from './middleware'; +import { AppAction } from '../action'; +import { coreMock } from 'src/core/public/mocks'; +import { AlertData, AlertResultList } from '../../../../../common/types'; +import { createBrowserHistory } from 'history'; +import { mockAlertResultList } from './mock_alert_result_list'; +import { HttpHandler, HttpResponse } from 'src/core/public/http'; + +describe('alert details tests', () => { + let store: Store; + let coreStart: ReturnType; + let history: History; + beforeEach(() => { + coreStart = coreMock.createStart(); + history = createBrowserHistory(); + const middleware = alertMiddlewareFactory(coreStart); + store = createStore(alertListReducer, applyMiddleware(middleware)); + }); + describe('when the user is on the alert list page with a selected alert in the url', () => { + beforeEach(() => { + const implementation: HttpHandler = async path => { + // if (path.contains('q9ncfh4q9ctrmc90umcq4')) { + // const response: AlertData = mockAlertResultList().alerts[0]; + // return response; + // } + // const response: AlertResultList = mockAlertResultList(); + // return response; + }; + coreStart.http.get.mockImplementation(implementation); + + // Simulates user navigating to the /alerts page + store.dispatch({ + type: 'userChangedUrl', + payload: { + ...history.location, + pathname: '/alerts', + search: '?selected_alert=q9ncfh4q9ctrmc90umcq4', + }, + }); + }); + + it('should return alert details data', () => { + const actualResponse = store.getState().alertDetails; + expect(actualResponse).not.toBeUndefined(); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts index 3f0a1a4a094f1..7db94fc9d4266 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts @@ -26,36 +26,158 @@ export const mockAlertResultList: (options?: { const alerts = []; for (let index = 0; index < actualCountToReturn; index++) { alerts.push({ - // TODO: Update this data to the new type '@timestamp': 1542341895000, id: 'xDUYMHABAJk0XnHd8rrd', agent: { id: 'ced9c68e-b94a-4d66-bb4c-6106514f0a2f', version: '3.0.0', }, - event: { - id: '2f1c0928-3876-4e11-acbb-9199257c7b1c', - action: 'open', - }, - file_classification: { - malware_classification: { - score: 3, - }, - }, - process: { - pid: 107, - unique_pid: 1, - }, host: { + id: 'xrctvybuni', hostname: 'HD-c15-bc09190a', - ip: '10.179.244.14', + ip: ['10.179.244.14'], + mac: ['xsertcyvbunimkn56edtyf'], os: { - name: 'Windows', + full: 'Windows 10', + name: 'windows', + version: '10', + variant: '3', }, }, thread: {}, prev: null, next: null, + event: { + id: '2f1c0928-3876-4e11-acbb-9199257c7b1c', + action: 'creation', + category: 'malware', + dataset: 'endpoint', + kind: 'alert', + module: 'endpoint', + type: 'creation', + }, + file: { + accessed: 1542789400, + created: 1542789400, + hash: { + md5: '4ace3baaa509d08510405e1b169e325b', + sha1: '27fb21cf5db95ffca43b234affa99becc4023b9d', + sha256: '6ed1c836dbf099be7845bdab7671def2c157643761b52251e04e9b6ee109ec75', + }, + pe: { + imphash: '835d619dfdf3cc727cebd91300ab3462', + }, + mtime: 1542789400, + owner: 'Administrators', + name: 'test name', + path: 'C:\\Windows\\TEMP\\tmp0000008f\\tmp00001be5', + size: 188416, + code_signature: { + subject_name: 'Cybereason Inc', + trusted: false, + }, + malware_classifier: { + features: { + data: { + buffer: + 'eAHtnU1oHHUUwHsQ7MGDiIIUD4sH8WBBxJtopiLoUY0pYo2ZTbJJ0yQ17m4+ms/NRzeVWpuUWCL4sWlEYvFQ8KJQ6NCTEA8eRD30sIo3PdSriLi7837Pko3LbHZ2M5m+XObHm/d/X////83O7jCZvzacHBpPplNdfalkdjSdyty674Ft59dN71Dpb9v5eKh8LMEHjsCF2wIfVlRKsHROYPGkQO5+gY2vBSYYdWZFYGwEO/cITHMqkxPYnBBY+07gtCuQ9gSGigJ5lPPYGXcE+jA4z3Ad1ZtAUiDUyrEEPYzqRnIKgxd/Rgc7gygPo5wn95PouN7OeEYJ1UXiJgRmvscgp/LOziIkkSyT+xRVnXhZ4DKh5goCkzidRHkGO4uvCyw9LDDtCay8ILCAzrJOJaGuZwUuvSewivJVIPsklq8JbL4qMJsTSCcExrGs83WKU295ZFo5lr2TaZbcUw5FeJy8tgTeLpCy2iGeS67ABXzlgbEi1UC5FxcZnA4y/CLK82Qxi847FGGZRTLsCUxR1aWEwOp1AmOjDRYYzgwusL9WfqBiGJxnVAanixTq7Dp22LBdlWMJzlOx8wmBK2Rx5WmBLJIRwtAijOQE+ooCb2B5xBOYRtlfNeXpLpA7oyZRTqHzGenkmIJPnhBIMrzTwSA6H93CO5l+c1NA99f6IwLH8fUKdjTmDpTbgS50+gGVnECnE4PpooC2guPoaPADSHrcncNHmEHtAFkq3+EI+A37zsrrTvH3WTkvJLoOTyBp10wx2JcgVCRahA4NrICE4a+hrMXsA3qAHItW188E8ejO7XV3eh/KCYwxlamEwCgL8lN2wTntfrhY/U0g/5KAdvUpT+AszWqBdqH7VLeeZrExK9Cv1UgIDKA8g/cx7QAEP+AhAfRaMKB2HOJh+BSFSqKjSytNGBlc6PrpxvK7lCVDxbSG3Z7AhCMwx6gelwgLAltXBXJUTH29j+U1LHdipx/QprfKfGnF0sBpdBYxmEQyTzW0h6/0khcuhhJYRufym+i4VKMocJMs/KvfoW3/UJb4PeZOSZVONThZz4djP/75TAXa/CVfOvX3RgVLIDreLPN1pP1osW7lGmHsEhjBOzf+EPBE4vndvWz5xb/cChxGcv1LAb+tluALKnZ47isf1MXvz1ZMlsCXbXtPceqhrcp1ps6YHwQeBXLEPCf7q23tl9uJui0bGBgYRAccv7uXr/g5Af+2oNTrpgTa/vnpjBvpLAwM4gRBPvIZGBgYGBgYGBgYGBgYGBgYGBgYGBgYNAOc9oMXs4GBgYFBcNBnww5QzDXgRtPSaZ5lg/itsRaslgZ3bnWEEVnhMetIBwiiVnlbCbWrEftrt11zdwWnseFW1QO63w1is3ptD1pV9xG0t+zvfUrzrvh380qwXWAVCw6h78GIfG7ZlzltXu6hd+y92fECRFhjuH3bXG8N43oXEHperdzvUbteaDxhVTUeq25fqhG1X6Ai8mtF6BDXz2wR+dzSgg4Qsxls5T11XMG+82y8GkG+b7kL69xg7mF1SFvhBgYGsYH/Xi7HE+PVkiB2jt1bNZxT+k4558jR53ydz5//1m1KOgYGBgYGBgYGEQfnsYaG2z1sdPJS79XQSu91ndobOAHCaN5vNzUk1bceQVzUpbw3iOuT+UFmR18bHrp3gyhDC56lCd1y85w2+HSNUwVhhdGC7blLf+bV/fqtvhMg1NDjCcugB1QXswbs8ekj/v1BgzFHBIIsyP+HfwFdMpzu', + decompressed_size: 27831, + encoding: 'zlib', + }, + }, + identifier: 'endpointpe', + score: 1, + threshold: 0.66, + version: '3.0.33', + }, + temp_file_path: 'C:\\Windows\\TEMP\\1bb9abfc-ca14-47b2-9f2c-10c323df42f9', + }, + process: { + pid: 1076, + ppid: 432, + entity_id: 'wertqwer', + parent: { + pid: 432, + entity_id: 'adsfsdaf', + }, + name: 'test name', + code_signature: { + subject_name: 'Cybereason Inc', + trusted: true, + }, + command_line: '"C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe"', + domain: 'NT AUTHORITY', + executable: 'C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe', + hash: { + md5: '1f2d082566b0fc5f2c238a5180db7451', + sha1: 'ca85243c0af6a6471bdaa560685c51eefd6dbc0d', + sha256: '8ad40c90a611d36eb8f9eb24fa04f7dbca713db383ff55a03aa0f382e92061a2', + }, + pe: { + imphash: 'c30d230b81c734e82e86e2e2fe01cd01', + }, + malware_classifier: { + identifier: 'Whitelisted', + score: 0, + threshold: 0, + version: '3.0.0', + }, + thread: [ + { + id: 1652, + service_name: 'CybereasonAntiMalware', + start: 1542788400, + start_address: 8791698721056, + start_address_module: 'C:\\Program Files\\Cybereason ActiveProbe\\gzfltum.dll', + }, + ], + sid: 'S-1-5-18', + start: 1542788400, + token: { + domain: 'NT AUTHORITY', + integrity_level: 16384, + integrity_level_name: 'system', + privileges: [ + { + description: 'Replace a process level token', + enabled: false, + name: 'SeAssignPrimaryTokenPrivilege', + }, + ], + sid: 'S-1-5-18', + type: 'tokenPrimary', + user: 'SYSTEM', + }, + uptime: 1025, + user: 'SYSTEM', + }, + dll: [ + { + pe: { + architecture: 'x64', + imphash: 'c30d230b81c734e82e86e2e2fe01cd01', + }, + code_signature: { + subject_name: 'Cybereason Inc', + trusted: true, + }, + compile_time: 1534424710, + hash: { + md5: '1f2d082566b0fc5f2c238a5180db7451', + sha1: 'ca85243c0af6a6471bdaa560685c51eefd6dbc0d', + sha256: '8ad40c90a611d36eb8f9eb24fa04f7dbca713db383ff55a03aa0f382e92061a2', + }, + malware_classifier: { + identifier: 'Whitelisted', + score: 0, + threshold: 0, + version: '3.0.0', + }, + mapped_address: 5362483200, + mapped_size: 0, + path: 'C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe', + }, + ], }); } const mock: AlertResultList = { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx index 0e575d64f6d43..7e3b13c70632c 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx @@ -21,7 +21,7 @@ export const HostAccordion = memo(({ alertData }: { alertData: Immutable Date: Fri, 28 Feb 2020 17:10:23 -0500 Subject: [PATCH 12/16] i dont feel sorry for you at all. dont unit test middleware unless you want to use redux saga --- .../store/alerts/alert_details.test.ts | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_details.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_details.test.ts index cf9b30ae99179..4edc31831eb14 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_details.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/alert_details.test.ts @@ -11,32 +11,40 @@ import { AlertListState } from '../../types'; import { alertMiddlewareFactory } from './middleware'; import { AppAction } from '../action'; import { coreMock } from 'src/core/public/mocks'; -import { AlertData, AlertResultList } from '../../../../../common/types'; import { createBrowserHistory } from 'history'; -import { mockAlertResultList } from './mock_alert_result_list'; -import { HttpHandler, HttpResponse } from 'src/core/public/http'; describe('alert details tests', () => { let store: Store; let coreStart: ReturnType; let history: History; + /** + * A function that waits until a selector returns true. + */ + let selectorIsTrue: (selector: (state: AlertListState) => boolean) => Promise; beforeEach(() => { coreStart = coreMock.createStart(); history = createBrowserHistory(); const middleware = alertMiddlewareFactory(coreStart); store = createStore(alertListReducer, applyMiddleware(middleware)); + + selectorIsTrue = async selector => { + // If the selector returns true, we're done + while (selector(store.getState()) !== true) { + // otherwise, wait til the next state change occurs + await new Promise(resolve => { + const unsubscribe = store.subscribe(() => { + unsubscribe(); + resolve(); + }); + }); + } + }; }); describe('when the user is on the alert list page with a selected alert in the url', () => { beforeEach(() => { - const implementation: HttpHandler = async path => { - // if (path.contains('q9ncfh4q9ctrmc90umcq4')) { - // const response: AlertData = mockAlertResultList().alerts[0]; - // return response; - // } - // const response: AlertResultList = mockAlertResultList(); - // return response; - }; - coreStart.http.get.mockImplementation(implementation); + const firstResponse: Promise = Promise.resolve(1); + const secondResponse: Promise = Promise.resolve(2); + coreStart.http.get.mockReturnValueOnce(firstResponse).mockReturnValueOnce(secondResponse); // Simulates user navigating to the /alerts page store.dispatch({ @@ -49,9 +57,9 @@ describe('alert details tests', () => { }); }); - it('should return alert details data', () => { - const actualResponse = store.getState().alertDetails; - expect(actualResponse).not.toBeUndefined(); + it('should return alert details data', async () => { + // wait for alertDetails to be defined + await selectorIsTrue(state => state.alertDetails !== undefined); }); }); }); From 3854b0251da86260555b3942498e934f64b0347d Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Sat, 29 Feb 2020 02:21:49 -0500 Subject: [PATCH 13/16] addresses comments --- .../endpoint/view/alerts/index.tsx | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx index 0a5f6cdbd994a..4bddc08778711 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx @@ -21,9 +21,10 @@ import { EuiPageContentHeader, EuiPageContentHeaderSection, EuiPageContentBody, + EuiLink, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useHistory, Link } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import { urlFromQueryParams } from './url_from_query_params'; import { AlertData } from '../../../../../common/types'; @@ -141,9 +142,11 @@ export const AlertIndex = memo(() => { const row = alertListData[rowIndex % pageSize]; if (columnId === 'alert_type') { return ( - + history.push(urlFromQueryParams({ ...queryParams, selected_alert: row.id })) + } > {i18n.translate( 'xpack.endpoint.application.endpoint.alerts.alertType.maliciousFileDescription', @@ -151,7 +154,7 @@ export const AlertIndex = memo(() => { defaultMessage: 'Malicious File', } )} - + ); } else if (columnId === 'event_type') { return row.event.action; @@ -184,7 +187,7 @@ export const AlertIndex = memo(() => { } return null; }; - }, [alertListData, timestampForRows, pageSize, queryParams, total]); + }, [total, alertListData, pageSize, history, queryParams, timestampForRows]); const pagination = useMemo(() => { return { @@ -196,6 +199,14 @@ export const AlertIndex = memo(() => { }; }, [onChangeItemsPerPage, onChangePage, pageIndex, pageSize]); + const columnVisibility = useMemo( + () => ({ + visibleColumns, + setVisibleColumns, + }), + [setVisibleColumns, visibleColumns] + ); + const selectedAlertData = useAlertListSelector(selectors.selectedAlertDetailsData); return ( @@ -236,13 +247,7 @@ export const AlertIndex = memo(() => { aria-label="Alert List" rowCount={total} columns={columns} - columnVisibility={useMemo( - () => ({ - visibleColumns, - setVisibleColumns, - }), - [setVisibleColumns, visibleColumns] - )} + columnVisibility={columnVisibility} renderCellValue={renderCellValue} pagination={pagination} data-test-subj="alertListGrid" From ce5d25b9902a0cfe5a4bf5c199835ff8f8ad0523 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 2 Mar 2020 10:53:22 -0500 Subject: [PATCH 14/16] addresses comments --- .../endpoint/view/alerts/details/overview/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx index fbf43bea78910..043933a91e9ce 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx @@ -64,7 +64,7 @@ export const AlertDetailsOverview = memo(() => {

, From 55001674b41f90b6ae8d407a0f5958a6c3db62be Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 2 Mar 2020 13:06:49 -0500 Subject: [PATCH 15/16] integrates with resolver pr --- .../endpoint/store/alerts/selectors.ts | 13 +++++--- .../view/alerts/details/overview/index.tsx | 13 ++++++-- .../endpoint/view/alerts/index.tsx | 1 - .../endpoint/view/alerts/resolver.tsx | 33 +++++++++---------- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts index d81bf3e34c85b..7ce7c2d08691e 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts @@ -15,7 +15,7 @@ import { AlertsAPIQueryParams, CreateStructuredSelector, } from '../../types'; -import { Immutable, LegacyEndpointEvent } from '../../../../../common/types'; +import { Immutable } from '../../../../../common/types'; const createStructuredSelector: CreateStructuredSelector = createStructuredSelectorWithBadType; /** @@ -98,6 +98,11 @@ export const hasSelectedAlert: (state: AlertListState) => boolean = createSelect /** * Determine if the alert event is most likely compatible with LegacyEndpointEvent. */ -function isAlertEventLegacyEndpointEvent(event: { endgame?: {} }): event is LegacyEndpointEvent { - return event.endgame !== undefined && 'unique_pid' in event.endgame; -} +export const selectedAlertIsLegacyEndpointEvent: ( + state: AlertListState +) => boolean = createSelector(selectedAlertDetailsData, function(event) { + if (event === undefined) { + return false; + } + return 'endgame' in event; +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx index 043933a91e9ce..080c70ca43bae 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx @@ -11,12 +11,16 @@ import { useAlertListSelector } from '../../hooks/use_alerts_selector'; import * as selectors from '../../../../store/alerts/selectors'; import { MetadataPanel } from './metadata_panel'; import { FormattedDate } from '../../formatted_date'; +import { AlertDetailResolver } from '../../resolver'; export const AlertDetailsOverview = memo(() => { const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); if (alertDetailsData === undefined) { return null; } + const selectedAlertIsLegacyEndpointEvent = useAlertListSelector( + selectors.selectedAlertIsLegacyEndpointEvent + ); const tabs = useMemo(() => { return [ @@ -43,10 +47,15 @@ export const AlertDetailsOverview = memo(() => { defaultMessage: 'Resolver', } ), - content: 'Resolver', + content: ( + <> + + {selectedAlertIsLegacyEndpointEvent && } + + ), }, ]; - }, []); + }, [selectedAlertIsLegacyEndpointEvent]); return ( <> diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx index 4bddc08778711..5d405f8c6fbae 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx @@ -93,7 +93,6 @@ export const AlertIndex = memo(() => { const alertListData = useAlertListSelector(selectors.alertListData); const hasSelectedAlert = useAlertListSelector(selectors.hasSelectedAlert); const queryParams = useAlertListSelector(selectors.uiQueryParams); - const selectedEvent = useAlertListSelector(selectors.selectedEvent); const onChangeItemsPerPage = useCallback( newPageSize => { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx index fb2b08d343daf..45448b8b000de 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx @@ -16,25 +16,22 @@ import { useAlertListSelector } from './hooks/use_alerts_selector'; import * as selectors from '../../store/alerts/selectors'; export const AlertDetailResolver = styled( - React.memo( - ({ className, selectedEvent }: { className?: string; selectedEvent?: LegacyEndpointEvent }) => { - const context = useKibana(); - const { store } = storeFactory(context); - // Different shit - const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); - if (alertDetailsData === undefined) { - return null; - } - - return ( -

- - - -
- ); + React.memo(({ className }: { className?: string }) => { + const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); + if (alertDetailsData === undefined) { + return null; } - ) + const context = useKibana(); + const { store } = storeFactory(context); + + return ( +
+ + + +
+ ); + }) )` height: 100%; width: 100%; From 93f7cf0b4930179a6ba34f107dc7384eb2956b54 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 2 Mar 2020 15:47:17 -0500 Subject: [PATCH 16/16] addresses comments --- .../details/metadata/host_accordion.tsx | 2 +- .../endpoint/view/alerts/index.test.tsx | 3 -- .../endpoint/view/alerts/resolver.tsx | 30 ++++++++----------- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx index 7e3b13c70632c..4108781f0a79b 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/host_accordion.tsx @@ -21,7 +21,7 @@ export const HostAccordion = memo(({ alertData }: { alertData: Immutable { it('should show the flyout', async () => { await render().findByTestId('alertDetailFlyout'); }); - it('should render resolver', async () => { - await render().findByTestId('alertResolver'); - }); describe('when the user clicks the close button on the flyout', () => { let renderResult: reactTestingLibrary.RenderResult; beforeEach(async () => { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx index 45448b8b000de..ec1dab45d50ab 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx @@ -12,26 +12,22 @@ import { Resolver } from '../../../../embeddables/resolver/view'; import { EndpointPluginServices } from '../../../../plugin'; import { LegacyEndpointEvent } from '../../../../../common/types'; import { storeFactory } from '../../../../embeddables/resolver/store'; -import { useAlertListSelector } from './hooks/use_alerts_selector'; -import * as selectors from '../../store/alerts/selectors'; export const AlertDetailResolver = styled( - React.memo(({ className }: { className?: string }) => { - const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); - if (alertDetailsData === undefined) { - return null; - } - const context = useKibana(); - const { store } = storeFactory(context); + React.memo( + ({ className, selectedEvent }: { className?: string; selectedEvent?: LegacyEndpointEvent }) => { + const context = useKibana(); + const { store } = storeFactory(context); - return ( -
- - - -
- ); - }) + return ( +
+ + + +
+ ); + } + ) )` height: 100%; width: 100%;