From c171c0efa97be2ee77c249eb155076d840c3df40 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 29 Nov 2024 05:10:45 +1100 Subject: [PATCH] [8.x] [EDR Workflows] Endpoint Insights UI - Connector selection (#201109) (#202208) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Backport This will backport the following commits from `main` to `8.x`: - [[EDR Workflows] Endpoint Insights UI - Connector selection (#201109)](https://github.com/elastic/kibana/pull/201109) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Konrad Szwarc --- .../impl/assistant_context/constants.tsx | 1 + .../packages/kbn-elastic-assistant/index.ts | 1 + .../components/insights/workflow_insights.tsx | 57 +++++++++++ .../insights/workflow_insights_results.tsx | 79 +++++++++++++++ .../insights/workflow_insights_scan.tsx | 99 +++++++++++++++++++ .../view/details/endpoint_details_content.tsx | 4 +- .../pages/endpoint_hosts/view/translations.ts | 31 ++++++ 7 files changed, 270 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/insights/workflow_insights.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/insights/workflow_insights_results.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/insights/workflow_insights_scan.tsx diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/constants.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/constants.tsx index c2ec745cc5c64..63c8adf37e5b8 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/constants.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant_context/constants.tsx @@ -8,6 +8,7 @@ import { KnowledgeBaseConfig } from '../assistant/types'; export const ATTACK_DISCOVERY_STORAGE_KEY = 'attackDiscovery'; +export const DEFEND_INSIGHTS_STORAGE_KEY = 'defendInsights'; export const DEFAULT_ASSISTANT_NAMESPACE = 'elasticAssistantDefault'; export const LAST_CONVERSATION_ID_LOCAL_STORAGE_KEY = 'lastConversationId'; export const MAX_ALERTS_LOCAL_STORAGE_KEY = 'maxAlerts'; diff --git a/x-pack/packages/kbn-elastic-assistant/index.ts b/x-pack/packages/kbn-elastic-assistant/index.ts index 7ec65c9601268..7f9ce6fe36c2f 100644 --- a/x-pack/packages/kbn-elastic-assistant/index.ts +++ b/x-pack/packages/kbn-elastic-assistant/index.ts @@ -83,6 +83,7 @@ export { /** The default maximum number of alerts to be sent as context when generating Attack discoveries */ DEFAULT_ATTACK_DISCOVERY_MAX_ALERTS, DEFAULT_LATEST_ALERTS, + DEFEND_INSIGHTS_STORAGE_KEY, KNOWLEDGE_BASE_LOCAL_STORAGE_KEY, /** The local storage key that specifies the maximum number of alerts to send as context */ MAX_ALERTS_LOCAL_STORAGE_KEY, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/insights/workflow_insights.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/insights/workflow_insights.tsx new file mode 100644 index 0000000000000..1ee7600eda2ef --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/insights/workflow_insights.tsx @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiHorizontalRule, EuiAccordion, EuiSpacer, EuiText } from '@elastic/eui'; +import React from 'react'; +import { WorkflowInsightsResults } from './workflow_insights_results'; +import { WorkflowInsightsScanSection } from './workflow_insights_scan'; +import { useIsExperimentalFeatureEnabled } from '../../../../../../../common/hooks/use_experimental_features'; +import { WORKFLOW_INSIGHTS } from '../../../translations'; + +export const WorkflowInsights = () => { + const isWorkflowInsightsEnabled = useIsExperimentalFeatureEnabled('defendInsights'); + + if (!isWorkflowInsightsEnabled) { + return null; + } + + const results = null; + + const renderLastResultsCaption = () => { + if (!results) { + return null; + } + return ( + + {WORKFLOW_INSIGHTS.titleRight} + + ); + }; + + return ( + <> + +

{WORKFLOW_INSIGHTS.title}

+ + } + initialIsOpen + extraAction={renderLastResultsCaption()} + paddingSize={'none'} + > + + + + + +
+ + + ); +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/insights/workflow_insights_results.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/insights/workflow_insights_results.tsx new file mode 100644 index 0000000000000..cb2f0bc0889a1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/insights/workflow_insights_results.tsx @@ -0,0 +1,79 @@ +/* + * 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, { useState } from 'react'; +import styled from 'styled-components'; +import { + EuiButtonIcon, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPanel, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { WORKFLOW_INSIGHTS } from '../../../translations'; + +interface WorkflowInsightsResultsProps { + results: boolean; +} + +const CustomEuiCallOut = styled(EuiCallOut)` + & .euiButtonIcon { + margin-top: 5px; /* Lower the close button */ + } +`; + +export const WorkflowInsightsResults = ({ results }: WorkflowInsightsResultsProps) => { + const [showEmptyResultsCallout, setShowEmptyResultsCallout] = useState(true); + const hideEmptyStateCallout = () => setShowEmptyResultsCallout(false); + if (!results) { + return null; + } + + return ( + <> + +

{WORKFLOW_INSIGHTS.issues.title}

+
+ + + + + + + + + + + {'McAfee EndpointSecurity'} + + + {'Add McAfee as a trusted application'} + + + + + + + + + + {showEmptyResultsCallout && ( + + {WORKFLOW_INSIGHTS.issues.emptyResults} + + )} + + ); +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/insights/workflow_insights_scan.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/insights/workflow_insights_scan.tsx new file mode 100644 index 0000000000000..b8c51e004fd33 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/insights/workflow_insights_scan.tsx @@ -0,0 +1,99 @@ +/* + * 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, useMemo } from 'react'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; +import { + AssistantAvatar, + DEFEND_INSIGHTS_STORAGE_KEY, + ConnectorSelectorInline, + DEFAULT_ASSISTANT_NAMESPACE, + useLoadConnectors, +} from '@kbn/elastic-assistant'; +import { noop } from 'lodash/fp'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; +import { some } from 'lodash'; +import { useSpaceId } from '../../../../../../../common/hooks/use_space_id'; +import { WORKFLOW_INSIGHTS } from '../../../translations'; +import { useKibana } from '../../../../../../../common/lib/kibana'; + +export const WorkflowInsightsScanSection = () => { + const CONNECTOR_ID_LOCAL_STORAGE_KEY = 'connectorId'; + + const spaceId = useSpaceId() ?? 'default'; + const { http } = useKibana().services; + const { data: aiConnectors } = useLoadConnectors({ + http, + }); + + // Store the selected connector id in local storage so that it persists across page reloads + const [localStorageWorkflowInsightsConnectorId, setLocalStorageWorkflowInsightsConnectorId] = + useLocalStorage( + `${DEFAULT_ASSISTANT_NAMESPACE}.${DEFEND_INSIGHTS_STORAGE_KEY}.${spaceId}.${CONNECTOR_ID_LOCAL_STORAGE_KEY}` + ); + + const [connectorId, setConnectorId] = React.useState( + localStorageWorkflowInsightsConnectorId + ); + + const onConnectorIdSelected = useCallback( + (selectedConnectorId: string) => { + setConnectorId(selectedConnectorId); + setLocalStorageWorkflowInsightsConnectorId(selectedConnectorId); + }, + [setLocalStorageWorkflowInsightsConnectorId] + ); + + // Check if the selected connector exists in the list of connectors, i.e. it is not deleted + const connectorExists = useMemo( + () => some(aiConnectors, ['id', connectorId]), + [aiConnectors, connectorId] + ); + + // Render the scan button only if a connector is selected + const renderScanButton = useMemo(() => { + if (!connectorExists) { + return null; + } + return ( + + {WORKFLOW_INSIGHTS.scan.button} + + ); + }, [connectorExists]); + + return ( + + + + + + + + + +

{WORKFLOW_INSIGHTS.scan.title}

+
+
+
+
+ + + + + + {renderScanButton} + + +
+
+ ); +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx index 7f458953022c5..c329c5c603bf6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx @@ -11,11 +11,11 @@ import { EuiFlexItem, EuiHealth, EuiLink, - EuiSpacer, EuiText, } from '@elastic/eui'; import React, { memo, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { WorkflowInsights } from './components/insights/workflow_insights'; import { isPolicyOutOfDate } from '../../utils'; import { AgentStatus } from '../../../../../common/components/endpoint/agents/agent_status'; import type { HostInfo } from '../../../../../../common/endpoint/types'; @@ -184,7 +184,7 @@ export const EndpointDetailsContent = memo( return (
- +