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..f93721349fdac
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/endpoint/agent_status.tsx
@@ -0,0 +1,30 @@
+/*
+ * 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';
+
+export const AgentStatus = React.memo(({ hostStatus }: { hostStatus: HostStatus }) => {
+ 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 5578264152c39..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
@@ -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';
@@ -53,6 +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' },
{ id: 'user.name' },
{ id: SOURCE_IP_FIELD_NAME, fieldType: IP_FIELD_TYPE },
{ id: DESTINATION_IP_FIELD_NAME, fieldType: IP_FIELD_TYPE },
@@ -177,6 +178,24 @@ const AlertSummaryViewComponent: React.FC<{
timelineId,
]);
+ const agentId = useMemo(() => {
+ const findAgentId = find({ category: 'agent', field: 'agent.id' }, data)?.values;
+ return findAgentId ? findAgentId[0] : '';
+ }, [data]);
+
+ const agentStatusRow = {
+ title: i18n.AGENT_STATUS,
+ description: {
+ contextId: timelineId,
+ eventId,
+ fieldName: 'host.status',
+ value: agentId,
+ 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 +207,11 @@ const AlertSummaryViewComponent: React.FC<{
return (
<>
-
+
{maybeRule?.note && (
{i18n.INVESTIGATION_GUIDE}
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/detections/containers/detection_engine/alerts/api.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts
index 71eac547dcc8e..05706981a681d 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..7419727fff6a2 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
@@ -7,25 +7,27 @@
import { isEmpty } from 'lodash';
import { useEffect, useState } from 'react';
-import { Maybe } from '../../../../../../observability/common/typings';
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;
+ isIsolated: boolean;
+ agentStatus: HostStatus;
}
/*
- * 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 [isIsolated, setIsIsolated] = useState(false);
+ const [agentStatus, setAgentStatus] = useState(HostStatus.UNHEALTHY);
const [loading, setLoading] = useState(false);
const { addError } = useAppToasts();
@@ -38,6 +40,7 @@ export const useHostIsolationStatus = ({
const metadataResponse = await getHostMetadata({ agentId });
if (isMounted) {
setIsIsolated(isEndpointHostIsolated(metadataResponse.metadata));
+ setAgentStatus(metadataResponse.host_status);
}
} catch (error) {
addError(error.message, { title: ISOLATION_STATUS_FAILURE });
@@ -61,5 +64,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
new file mode 100644
index 0000000000000..16f11809dd72b
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/agent_statuses.tsx
@@ -0,0 +1,56 @@
+/*
+ * 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 { 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(
+ ({
+ fieldName,
+ contextId,
+ eventId,
+ value,
+ }: {
+ fieldName: string;
+ contextId: string;
+ eventId: string;
+ value: string;
+ }) => {
+ const { isIsolated, agentStatus } = useHostIsolationStatus({ agentId: value });
+ const isolationFieldName = 'host.isolation';
+ 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..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';
@@ -30,11 +32,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 +120,15 @@ const FormattedFieldValueComponent: React.FC<{
return (
);
+ } else if (fieldName === HOST_STATUS_FIELD_NAME) {
+ return (
+
+ );
} else if (
[
RULE_REFERENCE_FIELD_NAME,