diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/action_completion_return_button.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/action_completion_return_button.tsx
new file mode 100644
index 00000000000000..af05999b2de265
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/action_completion_return_button.tsx
@@ -0,0 +1,36 @@
+/*
+ * 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, { useCallback } from 'react';
+import { EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiText } from '@elastic/eui';
+
+export const ActionCompletionReturnButton = React.memo(
+ ({ onClick, buttonText }: { onClick: () => void; buttonText: string }) => {
+ const onClickCallback = useCallback(() => onClick(), [onClick]);
+
+ return (
+ <>
+
+
+
+
+
+ {buttonText}
+
+
+
+
+ >
+ );
+ }
+);
+
+ActionCompletionReturnButton.displayName = 'ActionCompletionReturnButton';
diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts
index bd8e23e3a4559f..24b94cd6212b7e 100644
--- a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts
+++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/index.ts
@@ -9,3 +9,4 @@ export * from './isolate_success';
export * from './isolate_form';
export * from './unisolate_form';
export * from './endpoint_host_isolation_status';
+export * from './action_completion_return_button';
diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_success.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_success.tsx
index 3459da068b2824..7f05e8eafac872 100644
--- a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_success.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/isolate_success.tsx
@@ -5,58 +5,84 @@
* 2.0.
*/
-import React, { memo, ReactNode } from 'react';
-import { EuiButtonEmpty, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
+import React, { memo, ReactNode, useMemo } from 'react';
+import { EuiCallOut, EuiText } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
import { GET_ISOLATION_SUCCESS_MESSAGE, GET_UNISOLATION_SUCCESS_MESSAGE } from './translations';
+import { useCasesFromAlerts } from '../../../../detections/containers/detection_engine/alerts/use_cases_from_alerts';
+import { CaseDetailsLink } from '../../../../common/components/links';
export interface EndpointIsolateSuccessProps {
hostName: string;
+ alertId?: string;
isolateAction?: 'isolateHost' | 'unisolateHost';
- completeButtonLabel: string;
- onComplete: () => void;
additionalInfo?: ReactNode;
}
+const CasesAdditionalInfo: React.FC<{ alertIdForCase: string }> = ({ alertIdForCase }) => {
+ const { casesInfo } = useCasesFromAlerts({ alertId: alertIdForCase });
+
+ const caseCount: number = useMemo(() => casesInfo.length, [casesInfo]);
+
+ const casesList = useMemo(
+ () =>
+ casesInfo.map((caseInfo, index) => {
+ return (
+
+
+
+
+
+ );
+ }),
+ [casesInfo]
+ );
+
+ return (
+ <>
+ {caseCount > 0 && (
+ <>
+
+
+
+
+
+
+
+
+ >
+ )}
+ >
+ );
+};
+
export const EndpointIsolateSuccess = memo(
- ({
- hostName,
- isolateAction = 'isolateHost',
- onComplete,
- completeButtonLabel,
- additionalInfo,
- }) => {
+ ({ hostName, alertId, isolateAction = 'isolateHost', additionalInfo }) => {
return (
- <>
-
- {additionalInfo}
-
-
-
-
-
- {completeButtonLabel}
-
-
-
-
- >
+
+ {alertId !== undefined ? CasesAdditionalInfo({ alertIdForCase: alertId }) : additionalInfo}
+
);
}
);
diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx
index 36443cc91f4e84..acbae367fe75e2 100644
--- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/index.tsx
@@ -7,11 +7,8 @@
import React, { useMemo } from 'react';
import { find } from 'lodash/fp';
-import { EuiText } from '@elastic/eui';
-import { FormattedMessage } from '@kbn/i18n/react';
import { Maybe } from '../../../../../observability/common/typings';
import { useCasesFromAlerts } from '../../containers/detection_engine/alerts/use_cases_from_alerts';
-import { CaseDetailsLink } from '../../../common/components/links';
import { TimelineEventsDetailsItem } from '../../../../common/search_strategy';
import { IsolateHost } from './isolate';
import { UnisolateHost } from './unisolate';
@@ -45,53 +42,10 @@ export const HostIsolationPanel = React.memo(
const { casesInfo } = useCasesFromAlerts({ alertId });
- // Cases related components to be used in both isolate and unisolate actions from the alert details flyout entry point
- const caseCount: number = useMemo(() => casesInfo.length, [casesInfo]);
-
- const casesList = useMemo(
- () =>
- casesInfo.map((caseInfo, index) => {
- return (
-
-
-
-
-
- );
- }),
- [casesInfo]
- );
-
- const associatedCases = useMemo(() => {
- if (caseCount > 0) {
- return (
- <>
-
-
-
-
-
-
-
-
- >
- );
- }
- }, [caseCount, casesList]);
-
return isolateAction === 'isolateHost' ? (
void;
successCallback?: () => void;
@@ -60,20 +58,14 @@ export const IsolateHost = React.memo(
const caseCount: number = useMemo(() => casesInfo.length, [casesInfo]);
- const hostIsolatedSuccess = useMemo(() => {
+ const hostIsolatedSuccessButton = useMemo(() => {
return (
- <>
-
-
- >
+
);
- }, [backToAlertDetails, hostName, cases]);
+ }, [backToAlertDetails]);
const hostNotIsolated = useMemo(() => {
return (
@@ -108,7 +100,7 @@ export const IsolateHost = React.memo(
caseCount,
]);
- return isIsolated ? hostIsolatedSuccess : hostNotIsolated;
+ return isIsolated ? hostIsolatedSuccessButton : hostNotIsolated;
}
);
diff --git a/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx b/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx
index 2b810dc16eec80..5cc862694bef83 100644
--- a/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/host_isolation/unisolate.tsx
@@ -5,14 +5,14 @@
* 2.0.
*/
-import React, { useMemo, useState, useCallback, ReactNode } from 'react';
+import React, { useMemo, useState, useCallback } from 'react';
import { EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { CASES_ASSOCIATED_WITH_ALERT, RETURN_TO_ALERT_DETAILS } from './translations';
import {
EndpointIsolatedFormProps,
- EndpointIsolateSuccess,
EndpointUnisolateForm,
+ ActionCompletionReturnButton,
} from '../../../common/components/endpoint/host_isolation';
import { useHostUnisolation } from '../../containers/detection_engine/alerts/use_host_unisolation';
import { CasesFromAlertsResponse } from '../../containers/detection_engine/alerts/types';
@@ -21,14 +21,12 @@ export const UnisolateHost = React.memo(
({
endpointId,
hostName,
- cases,
casesInfo,
cancelCallback,
successCallback,
}: {
endpointId: string;
hostName: string;
- cases: ReactNode;
casesInfo: CasesFromAlertsResponse;
cancelCallback: () => void;
successCallback?: () => void;
@@ -60,20 +58,14 @@ export const UnisolateHost = React.memo(
const caseCount: number = useMemo(() => casesInfo.length, [casesInfo]);
- const hostUnisolatedSuccess = useMemo(() => {
+ const hostUnisolatedSuccessButton = useMemo(() => {
return (
- <>
-
-
- >
+
);
- }, [backToAlertDetails, hostName, cases]);
+ }, [backToAlertDetails]);
const hostNotUnisolated = useMemo(() => {
return (
@@ -108,7 +100,7 @@ export const UnisolateHost = React.memo(
caseCount,
]);
- return isUnIsolated ? hostUnisolatedSuccess : hostNotUnisolated;
+ return isUnIsolated ? hostUnisolatedSuccessButton : hostNotUnisolated;
}
);
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_isolate_flyout_panel.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_isolate_flyout_panel.tsx
index 289c1efeab0411..527189a3ef394c 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_isolate_flyout_panel.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/endpoint_isolate_flyout_panel.tsx
@@ -9,8 +9,8 @@ import React, { memo, useCallback, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { Dispatch } from 'redux';
-import { i18n } from '@kbn/i18n';
import { EuiForm } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
import { HostMetadata } from '../../../../../../../common/endpoint/types';
import { BackToEndpointDetailsFlyoutSubHeader } from './back_to_endpoint_details_flyout_subheader';
import {
@@ -18,6 +18,7 @@ import {
EndpointIsolateForm,
EndpointIsolateSuccess,
EndpointUnisolateForm,
+ ActionCompletionReturnButton,
} from '../../../../../../common/components/endpoint/host_isolation';
import { FlyoutBodyNoTopPadding } from './flyout_body_no_top_padding';
import { getEndpointDetailsPath } from '../../../../../common/routing';
@@ -88,16 +89,20 @@ export const EndpointIsolationFlyoutPanel = memo<{
<>
+ {wasSuccessful && (
+
+ )}
{wasSuccessful ? (
-
) : (
= ({
const [isHostIsolationPanelOpen, setIsHostIsolationPanel] = useState(false);
- const [isolateAction, setIsolateAction] = useState('isolateHost');
+ const [isolateAction, setIsolateAction] = useState<'isolateHost' | 'unisolateHost'>(
+ 'isolateHost'
+ );
+
+ const [isIsolateActionSuccessBannerVisible, setIsIsolateActionSuccessBannerVisible] = useState(
+ false
+ );
const showAlertDetails = useCallback(() => {
setIsHostIsolationPanel(false);
+ setIsIsolateActionSuccessBannerVisible(false);
}, []);
const { isAllowed: isIsolationAllowed } = useIsolationPrivileges();
@@ -114,6 +122,16 @@ const EventDetailsPanelComponent: React.FC = ({
return findAgentVersion ? findAgentVersion[0] : '';
}, [detailsData]);
+ const alertId = useMemo(() => {
+ const findAlertId = find({ category: '_id', field: '_id' }, detailsData)?.values;
+ return findAlertId ? findAlertId[0] : '';
+ }, [detailsData]);
+
+ const hostName = useMemo(() => {
+ const findHostName = find({ category: 'host', field: 'host.name' }, detailsData)?.values;
+ return findHostName ? findHostName[0] : '';
+ }, [detailsData]);
+
const isolationSupported = isIsolationSupported({
osName: hostOsFamily,
version: agentVersion,
@@ -142,6 +160,7 @@ const EventDetailsPanelComponent: React.FC = ({
const caseDetailsRefresh = useWithCaseDetailsRefresh();
const handleIsolationActionSuccess = useCallback(() => {
+ setIsIsolateActionSuccessBannerVisible(true);
// If a case details refresh ref is defined, then refresh actions and comments
if (caseDetailsRefresh) {
caseDetailsRefresh.refreshUserActionsAndComments();
@@ -161,6 +180,13 @@ const EventDetailsPanelComponent: React.FC = ({
)}
+ {isIsolateActionSuccessBannerVisible && (
+
+ )}
{isHostIsolationPanelOpen ? (