Skip to content

Commit

Permalink
[8.x] [EDR Workflows] Endpoint Insights UI - Connector selection (ela…
Browse files Browse the repository at this point in the history
…stic#201109) (elastic#202208)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[EDR Workflows] Endpoint Insights UI - Connector selection
(elastic#201109)](elastic#201109)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Konrad
Szwarc","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-11-28T16:12:15Z","message":"[EDR
Workflows] Endpoint Insights UI - Connector selection
(elastic#201109)\n\n![Screenshot 2024-11-21 at 11
33\n15](https://github.com/user-attachments/assets/fce40723-034f-41fe-8363-1304db5711fa)\n\nThis
is the first part of the UI changes related to
[the\nepic](elastic/security-team#10730). This
PR\nintroduces a new “Issues” section on the endpoint details flyout
and\nfocuses specifically on the “Scan” subsection. The “Scan”
subsection\nfocuses on connector selection and adding new connectors. A
stub for the\nresults has been added, but implementing the results is
out of scope for\nthe ticket addressed in this PR. Testing should be
covered in the follow\nup
PR's.\n\n\nhttps://github.com/user-attachments/assets/400a71e2-8a39-4916-b539-6f1bf3293cbf\n\n---------\n\nCo-authored-by:
Tomasz Ciecierski
<[email protected]>","sha":"28905708d4fecf63484d929e6e4a74d573aa27f1","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Defend
Workflows","backport:prev-minor","v8.18.0"],"title":"[EDR Workflows]
Endpoint Insights UI - Connector
selection","number":201109,"url":"https://github.com/elastic/kibana/pull/201109","mergeCommit":{"message":"[EDR
Workflows] Endpoint Insights UI - Connector selection
(elastic#201109)\n\n![Screenshot 2024-11-21 at 11
33\n15](https://github.com/user-attachments/assets/fce40723-034f-41fe-8363-1304db5711fa)\n\nThis
is the first part of the UI changes related to
[the\nepic](elastic/security-team#10730). This
PR\nintroduces a new “Issues” section on the endpoint details flyout
and\nfocuses specifically on the “Scan” subsection. The “Scan”
subsection\nfocuses on connector selection and adding new connectors. A
stub for the\nresults has been added, but implementing the results is
out of scope for\nthe ticket addressed in this PR. Testing should be
covered in the follow\nup
PR's.\n\n\nhttps://github.com/user-attachments/assets/400a71e2-8a39-4916-b539-6f1bf3293cbf\n\n---------\n\nCo-authored-by:
Tomasz Ciecierski
<[email protected]>","sha":"28905708d4fecf63484d929e6e4a74d573aa27f1"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/201109","number":201109,"mergeCommit":{"message":"[EDR
Workflows] Endpoint Insights UI - Connector selection
(elastic#201109)\n\n![Screenshot 2024-11-21 at 11
33\n15](https://github.com/user-attachments/assets/fce40723-034f-41fe-8363-1304db5711fa)\n\nThis
is the first part of the UI changes related to
[the\nepic](elastic/security-team#10730). This
PR\nintroduces a new “Issues” section on the endpoint details flyout
and\nfocuses specifically on the “Scan” subsection. The “Scan”
subsection\nfocuses on connector selection and adding new connectors. A
stub for the\nresults has been added, but implementing the results is
out of scope for\nthe ticket addressed in this PR. Testing should be
covered in the follow\nup
PR's.\n\n\nhttps://github.com/user-attachments/assets/400a71e2-8a39-4916-b539-6f1bf3293cbf\n\n---------\n\nCo-authored-by:
Tomasz Ciecierski
<[email protected]>","sha":"28905708d4fecf63484d929e6e4a74d573aa27f1"}},{"branch":"8.x","label":"v8.18.0","branchLabelMappingKey":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Konrad Szwarc <[email protected]>
  • Loading branch information
kibanamachine and szwarckonrad authored Nov 28, 2024
1 parent b691d32 commit c171c0e
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
1 change: 1 addition & 0 deletions x-pack/packages/kbn-elastic-assistant/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<EuiText color={'subdued'} size={'xs'}>
{WORKFLOW_INSIGHTS.titleRight}
</EuiText>
);
};

return (
<>
<EuiAccordion
id={'workflow-insights-wrapper'}
buttonContent={
<EuiText size={'m'}>
<h4>{WORKFLOW_INSIGHTS.title}</h4>
</EuiText>
}
initialIsOpen
extraAction={renderLastResultsCaption()}
paddingSize={'none'}
>
<EuiSpacer size={'m'} />
<WorkflowInsightsScanSection />
<EuiSpacer size={'m'} />
<WorkflowInsightsResults results={true} />
<EuiHorizontalRule />
</EuiAccordion>
<EuiSpacer size="l" />
</>
);
};
Original file line number Diff line number Diff line change
@@ -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 (
<>
<EuiText size={'s'}>
<h4>{WORKFLOW_INSIGHTS.issues.title}</h4>
</EuiText>
<EuiSpacer size={'s'} />
<EuiPanel paddingSize="m" hasShadow={false} hasBorder>
<EuiFlexGroup alignItems={'center'} gutterSize={'m'}>
<EuiFlexItem grow={false}>
<EuiIcon type="warning" size="l" color="warning" />
</EuiFlexItem>

<EuiFlexItem>
<EuiText size="s">
<EuiText size={'s'}>
<strong>{'McAfee EndpointSecurity'}</strong>
</EuiText>
<EuiText size={'s'} color={'subdued'}>
{'Add McAfee as a trusted application'}
</EuiText>
</EuiText>
</EuiFlexItem>

<EuiFlexItem grow={false} style={{ marginLeft: 'auto' }}>
<EuiButtonIcon
iconType="popout"
aria-label="External link"
href="https://google.com"
target="_blank"
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
{showEmptyResultsCallout && (
<CustomEuiCallOut onDismiss={hideEmptyStateCallout} color={'success'}>
{WORKFLOW_INSIGHTS.issues.emptyResults}
</CustomEuiCallOut>
)}
</>
);
};
Original file line number Diff line number Diff line change
@@ -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<string>(
`${DEFAULT_ASSISTANT_NAMESPACE}.${DEFEND_INSIGHTS_STORAGE_KEY}.${spaceId}.${CONNECTOR_ID_LOCAL_STORAGE_KEY}`
);

const [connectorId, setConnectorId] = React.useState<string | undefined>(
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 (
<EuiFlexItem grow={false}>
<EuiButton size="s">{WORKFLOW_INSIGHTS.scan.button}</EuiButton>
</EuiFlexItem>
);
}, [connectorExists]);

return (
<EuiPanel paddingSize="m" hasShadow={false} hasBorder>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center" gutterSize="m">
<EuiFlexItem grow={false}>
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<AssistantAvatar size={'xs'} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="s">
<h4>{WORKFLOW_INSIGHTS.scan.title}</h4>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<ConnectorSelectorInline
onConnectorSelected={noop}
onConnectorIdSelected={onConnectorIdSelected}
selectedConnectorId={connectorId}
/>
</EuiFlexItem>
{renderScanButton}
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -184,7 +184,7 @@ export const EndpointDetailsContent = memo<EndpointDetailsContentProps>(

return (
<div>
<EuiSpacer size="s" />
<WorkflowInsights />
<EuiDescriptionList
columnWidths={[1, 3]}
compressed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,37 @@ export const OVERVIEW = i18n.translate('xpack.securitySolution.endpointDetails.o
defaultMessage: 'Overview',
});

export const WORKFLOW_INSIGHTS = {
title: i18n.translate('xpack.securitySolution.endpointDetails.workflowInsights.sectionTitle', {
defaultMessage: 'Issues',
}),
titleRight: i18n.translate(
'xpack.securitySolution.endpointDetails.workflowInsights.extraAction',
{
defaultMessage: 'Last scans: ',
}
),
scan: {
title: i18n.translate('xpack.securitySolution.endpointDetails.workflowInsights.scan.title', {
defaultMessage: 'AI-Powered issue scan',
}),
button: i18n.translate('xpack.securitySolution.endpointDetails.workflowInsights.scan.button', {
defaultMessage: 'Scan',
}),
},
issues: {
title: i18n.translate('xpack.securitySolution.endpointDetails.workflowInsights.issues.title', {
defaultMessage: 'Issues',
}),
emptyResults: i18n.translate(
'xpack.securitySolution.endpointDetails.workflowInsights.issues.emptyResults',
{
defaultMessage: 'No issues had been found',
}
),
},
};

export const ACTIVITY_LOG = {
tabTitle: i18n.translate('xpack.securitySolution.endpointDetails.responseActionsHistory', {
defaultMessage: 'Response actions history',
Expand Down

0 comments on commit c171c0e

Please sign in to comment.