From 5b1cc7493764af621c3c2ad4a064cbe40342863a Mon Sep 17 00:00:00 2001 From: Candace Park Date: Mon, 14 Jun 2021 13:36:36 -0400 Subject: [PATCH 1/4] wip agent status row --- .../event_details/alert_summary_view.tsx | 31 ++++++++++++++-- .../containers/detection_engine/alerts/api.ts | 10 ++---- .../alerts/use_host_isolation_status.tsx | 1 + .../body/renderers/agent_statuses.tsx | 35 +++++++++++++++++++ .../timeline/body/renderers/constants.tsx | 1 + .../body/renderers/formatted_field.tsx | 6 ++++ 6 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx index 5578264152c39..f1e3d08c2cc92 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx @@ -12,7 +12,7 @@ import { EuiDescriptionListTitle, EuiSpacer, } from '@elastic/eui'; -import { get, getOr } from 'lodash/fp'; +import { get, getOr, find } from 'lodash/fp'; import React, { useMemo } from 'react'; import styled from 'styled-components'; @@ -37,6 +37,7 @@ import { SummaryView } from './summary_view'; import { AlertSummaryRow, getSummaryColumns, SummaryRow } from './helpers'; import { useRuleAsync } from '../../../detections/containers/detection_engine/rules/use_rule_async'; import { LineClamp } from '../line_clamp'; +import { useHostIsolationStatus } from '../../../detections/containers/detection_engine/alerts/use_host_isolation_status'; const StyledEuiDescriptionList = styled(EuiDescriptionList)` padding: 24px 4px 4px; @@ -53,6 +54,7 @@ const fields = [ { id: 'signal.rule.severity', label: ALERTS_HEADERS_SEVERITY }, { id: 'signal.rule.risk_score', label: ALERTS_HEADERS_RISK_SCORE }, { id: 'host.name' }, + { id: 'host.status', label: 'a label' }, { id: 'user.name' }, { id: SOURCE_IP_FIELD_NAME, fieldType: IP_FIELD_TYPE }, { id: DESTINATION_IP_FIELD_NAME, fieldType: IP_FIELD_TYPE }, @@ -177,6 +179,27 @@ const AlertSummaryViewComponent: React.FC<{ timelineId, ]); + const agentId = useMemo(() => { + const findAgentId = find({ category: 'agent', field: 'agent.id' }, data)?.values; + return findAgentId ? findAgentId[0] : ''; + }, [data]); + + const { isIsolated = 'no' } = useHostIsolationStatus({ agentId }); + + const agentStatusRow = { + title: 'Agent Status', + description: { + contextId: timelineId, + eventId, + fieldName: 'host.status', + value: isIsolated, + fieldType: 'agentStatus', + linkValue: undefined, + }, + }; + + const summaryRowsWithAgentStatus = [...summaryRows, agentStatusRow]; + const ruleId = useMemo(() => { const item = data.find((d) => d.field === 'signal.rule.id'); return Array.isArray(item?.originalValue) @@ -188,7 +211,11 @@ const AlertSummaryViewComponent: React.FC<{ return ( <> - + {maybeRule?.note && ( {i18n.INVESTIGATION_GUIDE} diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts index 4edbd5ab7e180..b7950a1db927e 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts @@ -7,7 +7,7 @@ import { UpdateDocumentByQueryResponse } from 'elasticsearch'; import { getCasesFromAlertsUrl } from '../../../../../../cases/common'; -import { HostIsolationResponse, HostMetadataInfo } from '../../../../../common/endpoint/types'; +import { HostIsolationResponse, HostInfo } from '../../../../../common/endpoint/types'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL, DETECTION_ENGINE_SIGNALS_STATUS_URL, @@ -178,12 +178,8 @@ export const getCaseIdsFromAlertId = async ({ * * @param host id */ -export const getHostMetadata = async ({ - agentId, -}: { - agentId: string; -}): Promise => - KibanaServices.get().http.fetch( +export const getHostMetadata = async ({ agentId }: { agentId: string }): Promise => + KibanaServices.get().http.fetch( resolvePathVariables(HOST_METADATA_GET_ROUTE, { id: agentId }), { method: 'get' } ); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation_status.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation_status.tsx index f7894d4764275..6dfade192efb3 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation_status.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation_status.tsx @@ -36,6 +36,7 @@ export const useHostIsolationStatus = ({ const fetchData = async () => { try { const metadataResponse = await getHostMetadata({ agentId }); + console.log(metadataResponse); if (isMounted) { setIsIsolated(isEndpointHostIsolated(metadataResponse.metadata)); } diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx new file mode 100644 index 0000000000000..5c166fabd4d0f --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { DefaultDraggable } from '../../../../../common/components/draggables'; + +export const AgentStatuses = React.memo( + ({ + fieldName, + contextId, + eventId, + value, + }: { + fieldName: string; + contextId: string; + eventId: string; + value: string | number | undefined | null; + }) => { + const agentStatuses = `${value}`; + return ( + + ); + } +); + +AgentStatuses.displayName = 'AgentStatuses'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/constants.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/constants.tsx index b1ef634fe052f..761d82b482af2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/constants.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/constants.tsx @@ -16,3 +16,4 @@ export const REFERENCE_URL_FIELD_NAME = 'reference.url'; export const EVENT_URL_FIELD_NAME = 'event.url'; export const SIGNAL_RULE_NAME_FIELD_NAME = 'signal.rule.name'; export const SIGNAL_STATUS_FIELD_NAME = 'signal.status'; +export const HOST_STATUS_FIELD_NAME = 'host.status'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx index 12effcd3fa81f..1df3b8f6e616c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx @@ -30,11 +30,13 @@ import { REFERENCE_URL_FIELD_NAME, EVENT_URL_FIELD_NAME, SIGNAL_STATUS_FIELD_NAME, + HOST_STATUS_FIELD_NAME, GEO_FIELD_TYPE, } from './constants'; import { RenderRuleName, renderEventModule, renderUrl } from './formatted_field_helpers'; import { RuleStatus } from './rule_status'; import { HostName } from './host_name'; +import { AgentStatuses } from './agent_statuses'; // simple black-list to prevent dragging and dropping fields such as message name const columnNamesNotDraggable = [MESSAGE_FIELD_NAME]; @@ -116,6 +118,10 @@ const FormattedFieldValueComponent: React.FC<{ return ( ); + } else if (fieldName === HOST_STATUS_FIELD_NAME) { + return ( + + ); } else if ( [ RULE_REFERENCE_FIELD_NAME, From c2c35b7b4299b1cb8f2617a819c1b7f52c5f0970 Mon Sep 17 00:00:00 2001 From: Candace Park Date: Tue, 15 Jun 2021 17:47:30 -0400 Subject: [PATCH 2/4] modify existing hook, agent status and isolated status up --- .../components/endpoint/agent_status.tsx | 31 ++++++++++++++++ .../event_details/alert_summary_view.tsx | 10 ++--- .../alerts/use_host_isolation_status.tsx | 9 +++-- .../body/renderers/agent_statuses.tsx | 37 +++++++++++++++---- .../body/renderers/formatted_field.tsx | 9 ++++- 5 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx new file mode 100644 index 0000000000000..9a7af24dac328 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiBadge } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { HostStatus } from '../../../../common/endpoint/types'; +import { HOST_STATUS_TO_BADGE_COLOR } from '../../../management/pages/endpoint_hosts/view/host_constants'; +import { Maybe } from '../../../../../observability/common/typings'; + +export const AgentStatus = React.memo(({ hostStatus }: { hostStatus: Maybe }) => { + return ( + + + + ); +}); + +AgentStatus.displayName = 'AgentStatus'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx index f1e3d08c2cc92..9929a84501c55 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx @@ -37,7 +37,6 @@ import { SummaryView } from './summary_view'; import { AlertSummaryRow, getSummaryColumns, SummaryRow } from './helpers'; import { useRuleAsync } from '../../../detections/containers/detection_engine/rules/use_rule_async'; import { LineClamp } from '../line_clamp'; -import { useHostIsolationStatus } from '../../../detections/containers/detection_engine/alerts/use_host_isolation_status'; const StyledEuiDescriptionList = styled(EuiDescriptionList)` padding: 24px 4px 4px; @@ -54,7 +53,7 @@ const fields = [ { id: 'signal.rule.severity', label: ALERTS_HEADERS_SEVERITY }, { id: 'signal.rule.risk_score', label: ALERTS_HEADERS_RISK_SCORE }, { id: 'host.name' }, - { id: 'host.status', label: 'a label' }, + { id: 'host.status' }, { id: 'user.name' }, { id: SOURCE_IP_FIELD_NAME, fieldType: IP_FIELD_TYPE }, { id: DESTINATION_IP_FIELD_NAME, fieldType: IP_FIELD_TYPE }, @@ -184,16 +183,13 @@ const AlertSummaryViewComponent: React.FC<{ return findAgentId ? findAgentId[0] : ''; }, [data]); - const { isIsolated = 'no' } = useHostIsolationStatus({ agentId }); - const agentStatusRow = { - title: 'Agent Status', + title: 'Agent status', description: { contextId: timelineId, eventId, fieldName: 'host.status', - value: isIsolated, - fieldType: 'agentStatus', + value: agentId, linkValue: undefined, }, }; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation_status.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation_status.tsx index 6dfade192efb3..2724211b0697f 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation_status.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_host_isolation_status.tsx @@ -12,20 +12,23 @@ import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { getHostMetadata } from './api'; import { ISOLATION_STATUS_FAILURE } from './translations'; import { isEndpointHostIsolated } from '../../../../common/utils/validators'; +import { HostStatus } from '../../../../../common/endpoint/types'; interface HostIsolationStatusResponse { loading: boolean; isIsolated: Maybe; + agentStatus: Maybe; } /* - * Retrieves the current isolation status of a host */ + * Retrieves the current isolation status of a host and the agent/host status */ export const useHostIsolationStatus = ({ agentId, }: { agentId: string; }): HostIsolationStatusResponse => { const [isIsolated, setIsIsolated] = useState>(); + const [agentStatus, setAgentStatus] = useState(); const [loading, setLoading] = useState(false); const { addError } = useAppToasts(); @@ -36,9 +39,9 @@ export const useHostIsolationStatus = ({ const fetchData = async () => { try { const metadataResponse = await getHostMetadata({ agentId }); - console.log(metadataResponse); if (isMounted) { setIsIsolated(isEndpointHostIsolated(metadataResponse.metadata)); + setAgentStatus(metadataResponse.host_status); } } catch (error) { addError(error.message, { title: ISOLATION_STATUS_FAILURE }); @@ -62,5 +65,5 @@ export const useHostIsolationStatus = ({ isMounted = false; }; }, [addError, agentId]); - return { loading, isIsolated }; + return { loading, isIsolated, agentStatus }; }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx index 5c166fabd4d0f..ddb8674eb2c61 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx @@ -6,7 +6,11 @@ */ import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { DefaultDraggable } from '../../../../../common/components/draggables'; +import { EndpointHostIsolationStatus } from '../../../../../common/components/endpoint/host_isolation'; +import { useHostIsolationStatus } from '../../../../../detections/containers/detection_engine/alerts/use_host_isolation_status'; +import { AgentStatus } from '../../../../../common/components/endpoint/agent_status'; export const AgentStatuses = React.memo( ({ @@ -18,16 +22,33 @@ export const AgentStatuses = React.memo( fieldName: string; contextId: string; eventId: string; - value: string | number | undefined | null; + value: string; }) => { - const agentStatuses = `${value}`; + const { isIsolated, agentStatus } = useHostIsolationStatus({ agentId: value }); + const isolationFieldName = 'host.isolation'; return ( - + + + + + + + + + + + + ); } ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx index 1df3b8f6e616c..efb51916e3765 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx @@ -5,6 +5,8 @@ * 2.0. */ +/* eslint-disable complexity */ + import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { isNumber, isEmpty } from 'lodash/fp'; import React from 'react'; @@ -120,7 +122,12 @@ const FormattedFieldValueComponent: React.FC<{ ); } else if (fieldName === HOST_STATUS_FIELD_NAME) { return ( - + ); } else if ( [ From fdbac1495cbd5a3cd6d2bccf96a4aa9eba080085 Mon Sep 17 00:00:00 2001 From: Candace Park Date: Wed, 16 Jun 2021 15:15:40 -0400 Subject: [PATCH 3/4] isIsolated is a real value, i18n --- .../common/components/event_details/alert_summary_view.tsx | 2 +- .../public/common/components/event_details/translations.ts | 4 ++++ .../components/timeline/body/renderers/agent_statuses.tsx | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx index 9929a84501c55..e229c0c6fae49 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/alert_summary_view.tsx @@ -184,7 +184,7 @@ const AlertSummaryViewComponent: React.FC<{ }, [data]); const agentStatusRow = { - title: 'Agent status', + title: i18n.AGENT_STATUS, description: { contextId: timelineId, eventId, diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts b/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts index 1ff88d9c2018b..a28d1976ca940 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts @@ -99,3 +99,7 @@ export const NESTED_COLUMN = (field: string) => defaultMessage: 'The {field} field is an object, and is broken down into nested fields which can be added as column', }); + +export const AGENT_STATUS = i18n.translate('xpack.securitySolution.detections.alerts.agentStatus', { + defaultMessage: 'Agent status', +}); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx index ddb8674eb2c61..dae6af24bc688 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx @@ -45,7 +45,7 @@ export const AgentStatuses = React.memo( tooltipContent={isolationFieldName} value={`${isIsolated}`} > - + From 7a3c4cdee2723f3fb6fc9228aff7950d3434c649 Mon Sep 17 00:00:00 2001 From: Candace Park Date: Fri, 18 Jun 2021 00:04:37 -0400 Subject: [PATCH 4/4] remove maybes --- .../public/common/components/endpoint/agent_status.tsx | 3 +-- .../alerts/use_host_isolation_status.tsx | 9 ++++----- .../timeline/body/renderers/agent_statuses.tsx | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx index 9a7af24dac328..f93721349fdac 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx @@ -10,9 +10,8 @@ import { EuiBadge } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { HostStatus } from '../../../../common/endpoint/types'; import { HOST_STATUS_TO_BADGE_COLOR } from '../../../management/pages/endpoint_hosts/view/host_constants'; -import { Maybe } from '../../../../../observability/common/typings'; -export const AgentStatus = React.memo(({ hostStatus }: { hostStatus: Maybe }) => { +export const AgentStatus = React.memo(({ hostStatus }: { hostStatus: HostStatus }) => { return ( ; - agentStatus: Maybe; + isIsolated: boolean; + agentStatus: HostStatus; } /* @@ -27,8 +26,8 @@ export const useHostIsolationStatus = ({ }: { agentId: string; }): HostIsolationStatusResponse => { - const [isIsolated, setIsIsolated] = useState>(); - const [agentStatus, setAgentStatus] = useState(); + const [isIsolated, setIsIsolated] = useState(false); + const [agentStatus, setAgentStatus] = useState(HostStatus.UNHEALTHY); const [loading, setLoading] = useState(false); const { addError } = useAppToasts(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx index dae6af24bc688..16f11809dd72b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx @@ -45,7 +45,7 @@ export const AgentStatuses = React.memo( tooltipContent={isolationFieldName} value={`${isIsolated}`} > - +