From 18302ccf0186baf37e0d1cf9fb909e23aac53cc4 Mon Sep 17 00:00:00 2001 From: Kfir Peled <61654899+kfirpeled@users.noreply.github.com> Date: Mon, 23 Dec 2024 17:47:24 +0100 Subject: [PATCH] [Cloud Security] Added labels popover (#204954) ## Summary Added expand button for labels with popover: https://github.com/user-attachments/assets/80950f51-b45b-4174-9be2-267b6aca569b https://github.com/user-attachments/assets/690ef85b-be48-42df-bf00-02ee7d9303f2 **How to test** To test this PR you can run ``` yarn storybook cloud_security_posture_packages ``` To test e2e - Enable the feature flag `kibana.dev.yml`: ```yml uiSettings.overrides.securitySolution:enableVisualizationsInFlyout: true xpack.securitySolution.enableExperimental: ['graphVisualizationInFlyoutEnabled'] ``` - Load mocked data: ``` node scripts/es_archiver load x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit \ --es-url http://elastic:changeme@localhost:9200 \ --kibana-url http://elastic:changeme@localhost:5601 node scripts/es_archiver load x-pack/test/cloud_security_posture_functional/es_archives/security_alerts \ --es-url http://elastic:changeme@localhost:9200 \ --kibana-url http://elastic:changeme@localhost:5601 ``` - Make sure you include data from Oct 13 2024. (in the video I use Last year) ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed --------- Co-authored-by: Sean Rathier Co-authored-by: Brad White Co-authored-by: seanrathier Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> (cherry picked from commit fd47d2ec7ccd9188fa45af85d44a17d143f2e44a) --- .../graph/src/common/constants.ts | 1 + .../components/edge/deafult_edge.stories.tsx | 6 +- .../graph/src/components/graph/graph.tsx | 8 +- .../graph/graph_popover.stories.tsx | 20 +++- .../src/components/graph/graph_popover.tsx | 4 +- .../graph_investigation.tsx | 55 +++++++--- .../graph_label_expand_popover.tsx | 52 +++++++++ .../graph_node_expand_popover.tsx | 2 +- .../use_graph_label_expand_popover.tsx | 100 ++++++++++++++++++ .../graph/src/components/index.ts | 9 ++ .../src/components/mock/test_providers.tsx | 3 +- .../src/components/node/diamond_node.tsx | 5 +- .../src/components/node/edge_group_node.tsx | 5 +- .../src/components/node/ellipse_node.tsx | 5 +- .../src/components/node/hexagon_node.tsx | 5 +- .../graph/src/components/node/label.tsx | 2 +- .../graph/src/components/node/label_node.tsx | 49 ++++++++- .../src/components/node/pentagon_node.tsx | 5 +- .../src/components/node/rectangle_node.tsx | 5 +- .../components/node/shapes/diamond_shape.tsx | 8 +- .../components/node/shapes/ellipse_shape.tsx | 8 +- .../components/node/shapes/hexagon_shape.tsx | 8 +- .../components/node/shapes/pentagon_shape.tsx | 8 +- .../node/shapes/rectangle_shape.tsx | 8 +- .../graph/src/components/node/styles.tsx | 33 +++--- .../graph/src/components/styles.tsx | 8 +- .../graph/src/components/test_ids.ts | 5 + .../graph/src/components/types.ts | 1 + .../page_objects/expanded_flyout.ts | 15 ++- .../pages/alerts_flyout.ts | 13 +++ .../pages/events_flyout.ts | 13 +++ 31 files changed, 376 insertions(+), 93 deletions(-) create mode 100644 x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_label_expand_popover.tsx create mode 100644 x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/use_graph_label_expand_popover.tsx diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/common/constants.ts b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/common/constants.ts index 307cbd65123e4..cc727dea4c847 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/common/constants.ts +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/common/constants.ts @@ -10,3 +10,4 @@ export const EVENT_GRAPH_VISUALIZATION_API = '/internal/cloud_security_posture/g export const RELATED_ENTITY = 'related.entity' as const; export const ACTOR_ENTITY_ID = 'actor.entity.id' as const; export const TARGET_ENTITY_ID = 'target.entity.id' as const; +export const EVENT_ACTION = 'event.action' as const; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/edge/deafult_edge.stories.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/edge/deafult_edge.stories.tsx index b4f35af2054f4..8b3b36237a7a2 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/edge/deafult_edge.stories.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/edge/deafult_edge.stories.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useEffect, useMemo, useRef } from 'react'; +import React, { memo, useEffect, useMemo, useRef } from 'react'; import { ThemeProvider } from '@emotion/react'; import { ReactFlow, @@ -47,7 +47,7 @@ export default { const nodeTypes = { // eslint-disable-next-line react/display-name - default: React.memo((props: NodeProps) => { + default: memo>((props: NodeProps) => { return (
); - }) as React.FC>, + }), label: LabelNode, }; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph/graph.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph/graph.tsx index f08e40111b7f8..292c4c3645468 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph/graph.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph/graph.tsx @@ -82,13 +82,7 @@ const edgeTypes = { * * @returns {JSX.Element} The rendered Graph component. */ -export const Graph: React.FC = ({ - nodes, - edges, - interactive, - isLocked = false, - ...rest -}) => { +export const Graph = ({ nodes, edges, interactive, isLocked = false, ...rest }: GraphProps) => { const backgroundId = useGeneratedHtmlId(); const fitViewRef = useRef< ((fitViewOptions?: FitViewOptions | undefined) => Promise) | null diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph/graph_popover.stories.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph/graph_popover.stories.tsx index a4561f404829a..dea054e9a7cc1 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph/graph_popover.stories.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph/graph_popover.stories.tsx @@ -9,7 +9,7 @@ import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from ' import { ThemeProvider, css } from '@emotion/react'; import { Story } from '@storybook/react'; import { EuiListGroup, EuiHorizontalRule } from '@elastic/eui'; -import type { EntityNodeViewModel, NodeProps } from '..'; +import type { EntityNodeViewModel, LabelNodeViewModel, NodeProps } from '..'; import { Graph } from '..'; import { GraphPopover } from './graph_popover'; import { ExpandButtonClickCallback } from '../types'; @@ -179,9 +179,11 @@ const Template: Story = () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const nodeClickHandler = (...args: any[]) => popoverOpenWrapper(nodePopover.onNodeClick, ...args); - const nodes: EntityNodeViewModel[] = useMemo( - () => - (['hexagon', 'ellipse', 'rectangle', 'pentagon', 'diamond'] as const).map((shape, idx) => ({ + const nodes: Array = useMemo( + () => [ + ...( + ['hexagon', 'ellipse', 'rectangle', 'pentagon', 'diamond'] as const + ).map((shape, idx) => ({ id: `${idx}`, label: `Node ${idx}`, color: 'primary', @@ -191,6 +193,16 @@ const Template: Story = () => { expandButtonClick: expandButtonClickHandler, nodeClick: nodeClickHandler, })), + { + id: 'label', + label: 'Node 5', + color: 'primary', + interactive: true, + shape: 'label', + expandButtonClick: expandButtonClickHandler, + nodeClick: nodeClickHandler, + } as LabelNodeViewModel, + ], // eslint-disable-next-line react-hooks/exhaustive-deps [] ); diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph/graph_popover.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph/graph_popover.tsx index 65d0b5a2b89b8..cd2b0e45e0104 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph/graph_popover.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph/graph_popover.tsx @@ -22,13 +22,13 @@ export interface GraphPopoverProps closePopover: () => void; } -export const GraphPopover: React.FC = ({ +export const GraphPopover = ({ isOpen, anchorElement, closePopover, children, ...rest -}) => { +}: GraphPopoverProps) => { const { euiTheme } = useEuiTheme(); if (!anchorElement) { diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_investigation.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_investigation.tsx index bd57082ba4cb9..9e63541b29d1f 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_investigation.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_investigation.tsx @@ -21,11 +21,17 @@ import type { Filter, Query, TimeRange, PhraseFilter } from '@kbn/es-query'; import { css } from '@emotion/react'; import { getEsQueryConfig } from '@kbn/data-service'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { Graph } from '../../..'; +import { Graph, isEntityNode } from '../../..'; import { useGraphNodeExpandPopover } from './use_graph_node_expand_popover'; +import { useGraphLabelExpandPopover } from './use_graph_label_expand_popover'; import { useFetchGraphData } from '../../hooks/use_fetch_graph_data'; import { GRAPH_INVESTIGATION_TEST_ID } from '../test_ids'; -import { ACTOR_ENTITY_ID, RELATED_ENTITY, TARGET_ENTITY_ID } from '../../common/constants'; +import { + ACTOR_ENTITY_ID, + EVENT_ACTION, + RELATED_ENTITY, + TARGET_ENTITY_ID, +} from '../../common/constants'; const CONTROLLED_BY_GRAPH_INVESTIGATION_FILTER = 'graph-investigation'; @@ -112,17 +118,23 @@ const useGraphPopovers = ( }, }); + const labelExpandPopover = useGraphLabelExpandPopover({ + onShowEventsWithThisActionClick: (node) => { + setSearchFilters((prev) => addFilter(dataViewId, prev, EVENT_ACTION, node.data.label ?? '')); + }, + }); + const openPopoverCallback = useCallback( (cb: Function, ...args: unknown[]) => { - [nodeExpandPopover].forEach(({ actions: { closePopover } }) => { + [nodeExpandPopover, labelExpandPopover].forEach(({ actions: { closePopover } }) => { closePopover(); }); cb(...args); }, - [nodeExpandPopover] + [nodeExpandPopover, labelExpandPopover] ); - return { nodeExpandPopover, openPopoverCallback }; + return { nodeExpandPopover, labelExpandPopover, openPopoverCallback }; }; interface GraphInvestigationProps { @@ -160,7 +172,7 @@ interface GraphInvestigationProps { /** * Graph investigation view allows the user to expand nodes and view related entities. */ -export const GraphInvestigation: React.FC = memo( +export const GraphInvestigation = memo( ({ initialState: { dataView, originEventIds, timeRange: initialTimeRange }, }: GraphInvestigationProps) => { @@ -181,13 +193,17 @@ export const GraphInvestigation: React.FC = memo( [dataView, searchFilters, uiSettings] ); - const { nodeExpandPopover, openPopoverCallback } = useGraphPopovers( + const { nodeExpandPopover, labelExpandPopover, openPopoverCallback } = useGraphPopovers( dataView?.id ?? '', setSearchFilters ); - const expandButtonClickHandler = (...args: unknown[]) => + const nodeExpandButtonClickHandler = (...args: unknown[]) => openPopoverCallback(nodeExpandPopover.onNodeExpandButtonClick, ...args); - const isPopoverOpen = [nodeExpandPopover].some(({ state: { isOpen } }) => isOpen); + const labelExpandButtonClickHandler = (...args: unknown[]) => + openPopoverCallback(labelExpandPopover.onLabelExpandButtonClick, ...args); + const isPopoverOpen = [nodeExpandPopover, labelExpandPopover].some( + ({ state: { isOpen } }) => isOpen + ); const { data, refresh, isFetching } = useFetchGraphData({ req: { query: { @@ -206,13 +222,19 @@ export const GraphInvestigation: React.FC = memo( const nodes = useMemo(() => { return ( data?.nodes.map((node) => { - const nodeHandlers = - node.shape !== 'label' && node.shape !== 'group' - ? { - expandButtonClick: expandButtonClickHandler, - } - : undefined; - return { ...node, ...nodeHandlers }; + if (isEntityNode(node)) { + return { + ...node, + expandButtonClick: nodeExpandButtonClickHandler, + }; + } else if (node.shape === 'label') { + return { + ...node, + expandButtonClick: labelExpandButtonClickHandler, + }; + } + + return { ...node }; }) ?? [] ); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -275,6 +297,7 @@ export const GraphInvestigation: React.FC = memo( + ); } diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_label_expand_popover.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_label_expand_popover.tsx new file mode 100644 index 0000000000000..6064e1cf9087b --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_label_expand_popover.tsx @@ -0,0 +1,52 @@ +/* + * 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, { memo } from 'react'; +import { EuiListGroup } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ExpandPopoverListItem } from '../styles'; +import { GraphPopover } from '../../..'; +import { + GRAPH_LABEL_EXPAND_POPOVER_TEST_ID, + GRAPH_LABEL_EXPAND_POPOVER_SHOW_EVENTS_WITH_THIS_ACTION_ITEM_ID, +} from '../test_ids'; + +interface GraphLabelExpandPopoverProps { + isOpen: boolean; + anchorElement: HTMLElement | null; + closePopover: () => void; + onShowEventsWithThisActionClick: () => void; +} + +export const GraphLabelExpandPopover = memo( + ({ isOpen, anchorElement, closePopover, onShowEventsWithThisActionClick }) => { + return ( + + + + + + ); + } +); + +GraphLabelExpandPopover.displayName = 'GraphLabelExpandPopover'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_node_expand_popover.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_node_expand_popover.tsx index 2fd10aa5c8c29..5104dbaeed5fb 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_node_expand_popover.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/graph_node_expand_popover.tsx @@ -26,7 +26,7 @@ interface GraphNodeExpandPopoverProps { onShowActionsOnEntityClick: () => void; } -export const GraphNodeExpandPopover: React.FC = memo( +export const GraphNodeExpandPopover = memo( ({ isOpen, anchorElement, diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/use_graph_label_expand_popover.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/use_graph_label_expand_popover.tsx new file mode 100644 index 0000000000000..bd9215394b56f --- /dev/null +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/graph_investigation/use_graph_label_expand_popover.tsx @@ -0,0 +1,100 @@ +/* + * 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, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useGraphPopover } from '../../..'; +import type { ExpandButtonClickCallback, NodeProps } from '../types'; +import type { PopoverActions } from '../graph/use_graph_popover'; +import { GraphLabelExpandPopover } from './graph_label_expand_popover'; + +interface UseGraphLabelExpandPopoverArgs { + onShowEventsWithThisActionClick: (node: NodeProps) => void; +} + +export const useGraphLabelExpandPopover = ({ + onShowEventsWithThisActionClick, +}: UseGraphLabelExpandPopoverArgs) => { + const { id, state, actions } = useGraphPopover('label-expand-popover'); + const { openPopover, closePopover } = actions; + + const selectedNode = useRef(null); + const unToggleCallbackRef = useRef<(() => void) | null>(null); + const [pendingOpen, setPendingOpen] = useState<{ + node: NodeProps; + el: HTMLElement; + unToggleCallback: () => void; + } | null>(null); + + const closePopoverHandler = useCallback(() => { + selectedNode.current = null; + unToggleCallbackRef.current?.(); + unToggleCallbackRef.current = null; + closePopover(); + }, [closePopover]); + + const onLabelExpandButtonClick: ExpandButtonClickCallback = useCallback( + (e, node, unToggleCallback) => { + if (selectedNode.current?.id === node.id) { + // If the same node is clicked again, close the popover + closePopoverHandler(); + } else { + // Close the current popover if open + closePopoverHandler(); + + // Set the pending open state + setPendingOpen({ node, el: e.currentTarget, unToggleCallback }); + } + }, + [closePopoverHandler] + ); + + useEffect(() => { + // Open pending popover if the popover is not open + if (!state.isOpen && pendingOpen) { + const { node, el, unToggleCallback } = pendingOpen; + + selectedNode.current = node; + unToggleCallbackRef.current = unToggleCallback; + openPopover(el); + + setPendingOpen(null); + } + }, [state.isOpen, pendingOpen, openPopover]); + + const PopoverComponent = memo(() => ( + { + onShowEventsWithThisActionClick(selectedNode.current as NodeProps); + closePopoverHandler(); + }} + /> + )); + + PopoverComponent.displayName = GraphLabelExpandPopover.displayName; + + const actionsWithClose: PopoverActions = useMemo( + () => ({ + ...actions, + closePopover: closePopoverHandler, + }), + [actions, closePopoverHandler] + ); + + return useMemo( + () => ({ + onLabelExpandButtonClick, + PopoverComponent, + id, + actions: actionsWithClose, + state, + }), + [PopoverComponent, actionsWithClose, id, onLabelExpandButtonClick, state] + ); +}; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/index.ts b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/index.ts index d3cd397764e60..0183b6cd0420f 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/index.ts +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/index.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { NodeViewModel } from './types'; + export { Graph } from './graph/graph'; export { GraphInvestigation } from './graph_investigation/graph_investigation'; export { GraphPopover } from './graph/graph_popover'; @@ -18,3 +20,10 @@ export type { EntityNodeViewModel, NodeProps, } from './types'; + +export const isEntityNode = (node: NodeViewModel) => + node.shape === 'ellipse' || + node.shape === 'pentagon' || + node.shape === 'rectangle' || + node.shape === 'diamond' || + node.shape === 'hexagon'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/mock/test_providers.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/mock/test_providers.tsx index 3d07c1c6037ed..a529468fdc3fb 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/mock/test_providers.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/mock/test_providers.tsx @@ -13,8 +13,7 @@ interface Props { children?: React.ReactNode; } -/** A utility for wrapping children in the providers required to run most tests */ -export const TestProvidersComponent: React.FC = ({ children }) => { +export const TestProvidersComponent = ({ children }: Props) => { return ( ({ eui: euiDarkVars, darkMode: true })}>{children} ); diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/diamond_node.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/diamond_node.tsx index b46c44c69d1b8..7275727b70832 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/diamond_node.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/diamond_node.tsx @@ -25,8 +25,7 @@ import { Label } from './label'; const NODE_WIDTH = 99; const NODE_HEIGHT = 98; -// eslint-disable-next-line react/display-name -export const DiamondNode: React.FC = memo((props: NodeProps) => { +export const DiamondNode = memo((props: NodeProps) => { const { id, color, icon, label, interactive, expandButtonClick, nodeClick } = props.data as EntityNodeViewModel; const { euiTheme } = useEuiTheme(); @@ -87,3 +86,5 @@ export const DiamondNode: React.FC = memo((props: NodeProps) => { ); }); + +DiamondNode.displayName = 'DiamondNode'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/edge_group_node.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/edge_group_node.tsx index 10ac415398717..11babb4d33940 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/edge_group_node.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/edge_group_node.tsx @@ -10,8 +10,7 @@ import { Handle, Position } from '@xyflow/react'; import { HandleStyleOverride } from './styles'; import type { NodeProps } from '../types'; -// eslint-disable-next-line react/display-name -export const EdgeGroupNode: React.FC = memo((props: NodeProps) => { +export const EdgeGroupNode = memo((props: NodeProps) => { // Handles order horizontally is: in > inside > out > outside return ( <> @@ -46,3 +45,5 @@ export const EdgeGroupNode: React.FC = memo((props: NodeProps) => { ); }); + +EdgeGroupNode.displayName = 'EdgeGroupNode'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/ellipse_node.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/ellipse_node.tsx index 7830feb96a48c..b9c73c66685b9 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/ellipse_node.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/ellipse_node.tsx @@ -24,8 +24,7 @@ import { EllipseHoverShape, EllipseShape } from './shapes/ellipse_shape'; import { NodeExpandButton } from './node_expand_button'; import { Label } from './label'; -// eslint-disable-next-line react/display-name -export const EllipseNode: React.FC = memo((props: NodeProps) => { +export const EllipseNode = memo((props: NodeProps) => { const { id, color, icon, label, interactive, expandButtonClick, nodeClick } = props.data as EntityNodeViewModel; const { euiTheme } = useEuiTheme(); @@ -86,3 +85,5 @@ export const EllipseNode: React.FC = memo((props: NodeProps) => { ); }); + +EllipseNode.displayName = 'EllipseNode'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/hexagon_node.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/hexagon_node.tsx index ebde4e2334e21..e033dc0f23b32 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/hexagon_node.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/hexagon_node.tsx @@ -25,8 +25,7 @@ import { Label } from './label'; const NODE_WIDTH = 87; const NODE_HEIGHT = 96; -// eslint-disable-next-line react/display-name -export const HexagonNode: React.FC = memo((props: NodeProps) => { +export const HexagonNode = memo((props: NodeProps) => { const { id, color, icon, label, interactive, expandButtonClick, nodeClick } = props.data as EntityNodeViewModel; const { euiTheme } = useEuiTheme(); @@ -87,3 +86,5 @@ export const HexagonNode: React.FC = memo((props: NodeProps) => { ); }); + +HexagonNode.displayName = 'HexagonNode'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/label.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/label.tsx index 098a3e0dd89c7..a0ce16960b26c 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/label.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/label.tsx @@ -62,7 +62,7 @@ export interface LabelProps { text?: string; } -const LabelComponent: React.FC = ({ text = '' }: LabelProps) => { +const LabelComponent = ({ text = '' }: LabelProps) => { const [isTruncated, setIsTruncated] = React.useState(false); return ( diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/label_node.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/label_node.tsx index 62ee671659662..bc293d0cf5e9e 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/label_node.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/label_node.tsx @@ -7,19 +7,56 @@ import React, { memo } from 'react'; import { Handle, Position } from '@xyflow/react'; -import { LabelNodeContainer, LabelShape, HandleStyleOverride, LabelShapeOnHover } from './styles'; +import { css } from '@emotion/react'; +import { + LabelNodeContainer, + LabelShape, + HandleStyleOverride, + LabelShapeOnHover, + NodeButton, + LABEL_PADDING_X, + LABEL_BORDER_WIDTH, + LABEL_HEIGHT, +} from './styles'; import type { LabelNodeViewModel, NodeProps } from '../types'; +import { NodeExpandButton } from './node_expand_button'; +import { getTextWidth } from '../graph/utils'; -// eslint-disable-next-line react/display-name -export const LabelNode: React.FC = memo((props: NodeProps) => { - const { id, color, label, interactive } = props.data as LabelNodeViewModel; +const LABEL_MIN_WIDTH = 100; + +export const LabelNode = memo((props: NodeProps) => { + const { id, color, label, interactive, nodeClick, expandButtonClick } = + props.data as LabelNodeViewModel; + const text = label ? label : id; + const labelWidth = Math.max( + LABEL_MIN_WIDTH, + getTextWidth(text ?? '') + LABEL_PADDING_X * 2 + LABEL_BORDER_WIDTH * 2 + ); return ( {interactive && } - {label ? label : id} + {text} + {interactive && ( + <> + nodeClick?.(e, props)} + /> + expandButtonClick?.(e, props, unToggleCallback)} + x={`${labelWidth}px`} + y={`${-LABEL_HEIGHT + (LABEL_HEIGHT - NodeExpandButton.ExpandButtonSize) / 2}px`} + /> + + )} = memo((props: NodeProps) => { ); }); + +LabelNode.displayName = 'LabelNode'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/pentagon_node.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/pentagon_node.tsx index d54a2f808b6e7..d6708f4c9b0dd 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/pentagon_node.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/pentagon_node.tsx @@ -30,8 +30,7 @@ const PentagonShapeOnHover = styled(NodeShapeOnHoverSvg)` const NODE_WIDTH = 91; const NODE_HEIGHT = 88; -// eslint-disable-next-line react/display-name -export const PentagonNode: React.FC = memo((props: NodeProps) => { +export const PentagonNode = memo((props: NodeProps) => { const { id, color, icon, label, interactive, expandButtonClick, nodeClick } = props.data as EntityNodeViewModel; const { euiTheme } = useEuiTheme(); @@ -92,3 +91,5 @@ export const PentagonNode: React.FC = memo((props: NodeProps) => { ); }); + +PentagonNode.displayName = 'PentagonNode'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/rectangle_node.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/rectangle_node.tsx index f923641a25a50..99c336373b836 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/rectangle_node.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/rectangle_node.tsx @@ -25,8 +25,7 @@ import { Label } from './label'; const NODE_WIDTH = 81; const NODE_HEIGHT = 80; -// eslint-disable-next-line react/display-name -export const RectangleNode: React.FC = memo((props: NodeProps) => { +export const RectangleNode = memo((props: NodeProps) => { const { id, color, icon, label, interactive, expandButtonClick, nodeClick } = props.data as EntityNodeViewModel; const { euiTheme } = useEuiTheme(); @@ -87,3 +86,5 @@ export const RectangleNode: React.FC = memo((props: NodeProps) => { ); }); + +RectangleNode.displayName = 'RectangleNode'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/diamond_shape.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/diamond_shape.tsx index 0f0cce4e744c7..a0998ffd9a92f 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/diamond_shape.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/diamond_shape.tsx @@ -8,8 +8,7 @@ import React, { memo } from 'react'; import type { HoverShapeProps, ShapeProps } from './types'; -// eslint-disable-next-line react/display-name -export const DiamondHoverShape: React.FC = memo(({ stroke }) => ( +export const DiamondHoverShape = memo(({ stroke }) => ( = memo(({ stroke }) => strokeDasharray="2 2" /> )); +DiamondHoverShape.displayName = 'DiamondHoverShape'; -// eslint-disable-next-line react/display-name -export const DiamondShape: React.FC = memo(({ stroke, fill }) => ( +export const DiamondShape = memo(({ stroke, fill }) => ( )); +DiamondShape.displayName = 'DiamondShape'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/ellipse_shape.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/ellipse_shape.tsx index ef2f8452cf206..fdf699d1c3e05 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/ellipse_shape.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/ellipse_shape.tsx @@ -8,12 +8,12 @@ import React, { memo } from 'react'; import type { HoverShapeProps, ShapeProps } from './types'; -// eslint-disable-next-line react/display-name -export const EllipseHoverShape: React.FC = memo(({ stroke }) => ( +export const EllipseHoverShape = memo(({ stroke }) => ( )); +EllipseHoverShape.displayName = 'EllipseHoverShape'; -// eslint-disable-next-line react/display-name -export const EllipseShape: React.FC = memo(({ stroke, fill }) => ( +export const EllipseShape = memo(({ stroke, fill }) => ( )); +EllipseShape.displayName = 'EllipseShape'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/hexagon_shape.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/hexagon_shape.tsx index 133c6e4a035c7..4a0de92b75f65 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/hexagon_shape.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/hexagon_shape.tsx @@ -8,8 +8,7 @@ import React, { memo } from 'react'; import type { HoverShapeProps, ShapeProps } from './types'; -// eslint-disable-next-line react/display-name -export const HexagonHoverShape: React.FC = memo(({ stroke }) => ( +export const HexagonHoverShape = memo(({ stroke }) => ( = memo(({ stroke }) => strokeDasharray="2 2" /> )); +HexagonHoverShape.displayName = 'HexagonHoverShape'; -// eslint-disable-next-line react/display-name -export const HexagonShape: React.FC = memo(({ stroke, fill }) => ( +export const HexagonShape = memo(({ stroke, fill }) => ( )); +HexagonShape.displayName = 'HexagonShape'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/pentagon_shape.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/pentagon_shape.tsx index ea43efed36a48..f46aae508fbca 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/pentagon_shape.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/pentagon_shape.tsx @@ -8,8 +8,7 @@ import React, { memo } from 'react'; import type { HoverShapeProps, ShapeProps } from './types'; -// eslint-disable-next-line react/display-name -export const PentagonHoverShape: React.FC = memo(({ stroke }) => ( +export const PentagonHoverShape = memo(({ stroke }) => ( = memo(({ stroke }) = strokeDasharray="2 2" /> )); +PentagonHoverShape.displayName = 'PentagonHoverShape'; -// eslint-disable-next-line react/display-name -export const PentagonShape: React.FC = memo(({ stroke, fill }) => ( +export const PentagonShape = memo(({ stroke, fill }) => ( )); +PentagonShape.displayName = 'PentagonShape'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/rectangle_shape.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/rectangle_shape.tsx index 28e55c8ea117c..e714d66880589 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/rectangle_shape.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/shapes/rectangle_shape.tsx @@ -8,8 +8,7 @@ import React, { memo } from 'react'; import type { HoverShapeProps, ShapeProps } from './types'; -// eslint-disable-next-line react/display-name -export const RectangleHoverShape: React.FC = memo(({ stroke }) => ( +export const RectangleHoverShape = memo(({ stroke }) => ( = memo(({ stroke }) strokeDasharray="2 2" /> )); +RectangleHoverShape.displayName = 'RectangleHoverShape'; -// eslint-disable-next-line react/display-name -export const RectangleShape: React.FC = memo(({ stroke, fill }) => ( +export const RectangleShape = memo(({ stroke, fill }) => ( )); +RectangleShape.displayName = 'RectangleShape'; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/styles.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/styles.tsx index c4305c6fded6b..ae5e209c96f0e 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/styles.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/node/styles.tsx @@ -10,6 +10,7 @@ import styled from '@emotion/styled'; import { type EuiIconProps, type EuiTextProps, + type CommonProps, EuiButtonIcon, EuiIcon, EuiText, @@ -21,6 +22,7 @@ import { getSpanIcon } from './get_span_icon'; import type { NodeExpandButtonProps } from './node_expand_button'; import type { EntityNodeViewModel, LabelNodeViewModel } from '..'; +export const LABEL_HEIGHT = 24; export const LABEL_PADDING_X = 15; export const LABEL_BORDER_WIDTH = 1; export const NODE_WIDTH = 90; @@ -29,9 +31,10 @@ export const NODE_LABEL_WIDTH = 160; type NodeColor = EntityNodeViewModel['color'] | LabelNodeViewModel['color']; export const LabelNodeContainer = styled.div` + position: relative; text-wrap: nowrap; min-width: 100px; - height: 24px; + height: ${LABEL_HEIGHT}px; `; interface LabelShapeProps extends EuiTextProps { @@ -107,26 +110,28 @@ export const NodeShapeSvg = styled.svg` z-index: 1; `; -export interface NodeButtonProps { +export interface NodeButtonProps extends CommonProps { + width?: number; + height?: number; onClick?: (e: React.MouseEvent) => void; } -export const NodeButton: React.FC = ({ onClick }) => ( - - +export const NodeButton = ({ onClick, width, height, ...props }: NodeButtonProps) => ( + + ); -const StyledNodeContainer = styled.div` +const StyledNodeContainer = styled.div` position: absolute; - width: ${NODE_WIDTH}px; - height: ${NODE_HEIGHT}px; + width: ${(props) => props.width ?? NODE_WIDTH}px; + height: ${(props) => props.height ?? NODE_HEIGHT}px; z-index: 1; `; -const StyledNodeButton = styled.div` - width: ${NODE_WIDTH}px; - height: ${NODE_HEIGHT}px; +const StyledNodeButton = styled.div` + width: ${(props) => props.width ?? NODE_WIDTH}px; + height: ${(props) => props.height ?? NODE_HEIGHT}px; `; export const StyledNodeExpandButton = styled.div` @@ -142,7 +147,7 @@ export const StyledNodeExpandButton = styled.div` opacity: 1; } - ${NodeShapeContainer}:hover & { + ${NodeShapeContainer}:hover &, ${LabelNodeContainer}:hover & { opacity: 1; /* Show on hover */ } @@ -159,11 +164,11 @@ export const NodeShapeOnHoverSvg = styled(NodeShapeSvg)` opacity: 0; /* Hidden by default */ transition: opacity 0.2s ease; /* Smooth transition */ - ${NodeShapeContainer}:hover & { + ${NodeShapeContainer}:hover &, ${LabelNodeContainer}:hover & { opacity: 1; /* Show on hover */ } - ${NodeShapeContainer}:has(${StyledNodeExpandButton}.toggled) & { + ${NodeShapeContainer}:has(${StyledNodeExpandButton}.toggled) &, ${LabelNodeContainer}:has(${StyledNodeExpandButton}.toggled) & { opacity: 1; /* Show on hover */ } diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/styles.tsx b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/styles.tsx index be776d57be12a..fc7f4a8a69bcc 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/styles.tsx +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/styles.tsx @@ -55,15 +55,15 @@ const StyleEuiIcon = styled(EuiIcon)` type RoundedEuiIconProps = EuiIconProps & EuiColorProps; -const RoundedEuiIcon: React.FC = ({ color, background, ...rest }) => ( +const RoundedEuiIcon = ({ color, background, ...rest }: RoundedEuiIconProps) => ( ); -export const ExpandPopoverListItem: React.FC< - CommonProps & Pick -> = (props) => { +export const ExpandPopoverListItem = ( + props: CommonProps & Pick +) => { const { iconType, label, onClick, ...rest } = props; const { euiTheme } = useEuiTheme(); return ( diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/test_ids.ts b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/test_ids.ts index 96e399d670907..d507563c3fd73 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/test_ids.ts +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/test_ids.ts @@ -16,3 +16,8 @@ export const GRAPH_NODE_POPOVER_SHOW_ACTIONS_BY_ITEM_ID = `${GRAPH_INVESTIGATION_TEST_ID}ShowActionsByEntity` as const; export const GRAPH_NODE_POPOVER_SHOW_ACTIONS_ON_ITEM_ID = `${GRAPH_INVESTIGATION_TEST_ID}ShowActionsOnEntity` as const; + +export const GRAPH_LABEL_EXPAND_POPOVER_TEST_ID = + `${GRAPH_INVESTIGATION_TEST_ID}GraphLabelExpandPopover` as const; +export const GRAPH_LABEL_EXPAND_POPOVER_SHOW_EVENTS_WITH_THIS_ACTION_ITEM_ID = + `${GRAPH_INVESTIGATION_TEST_ID}ShowEventsWithThisAction` as const; diff --git a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/types.ts b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/types.ts index 32e34a212af59..dc5773cdff41d 100644 --- a/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/types.ts +++ b/x-pack/solutions/security/packages/kbn-cloud-security-posture/graph/src/components/types.ts @@ -51,6 +51,7 @@ export interface LabelNodeViewModel LabelNodeDataModel, BaseNodeDataViewModel { expandButtonClick?: ExpandButtonClickCallback; + nodeClick?: NodeClickCallback; } export type NodeViewModel = EntityNodeViewModel | GroupNodeViewModel | LabelNodeViewModel; diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/expanded_flyout.ts b/x-pack/test/cloud_security_posture_functional/page_objects/expanded_flyout.ts index 5829e9083a8cf..1bbd2c5091d64 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/expanded_flyout.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/expanded_flyout.ts @@ -17,6 +17,8 @@ const GRAPH_NODE_EXPAND_POPOVER_TEST_ID = `${GRAPH_INVESTIGATION_TEST_ID}GraphNo const GRAPH_NODE_POPOVER_EXPLORE_RELATED_TEST_ID = `${GRAPH_INVESTIGATION_TEST_ID}ExploreRelatedEntities`; const GRAPH_NODE_POPOVER_SHOW_ACTIONS_BY_TEST_ID = `${GRAPH_INVESTIGATION_TEST_ID}ShowActionsByEntity`; const GRAPH_NODE_POPOVER_SHOW_ACTIONS_ON_TEST_ID = `${GRAPH_INVESTIGATION_TEST_ID}ShowActionsOnEntity`; +const GRAPH_LABEL_EXPAND_POPOVER_TEST_ID = `${GRAPH_INVESTIGATION_TEST_ID}GraphLabelExpandPopover`; +const GRAPH_LABEL_EXPAND_POPOVER_SHOW_EVENTS_WITH_THIS_ACTION_ITEM_ID = `${GRAPH_INVESTIGATION_TEST_ID}ShowEventsWithThisAction`; type Filter = Parameters[0]; export class ExpandedFlyout extends FtrService { @@ -52,11 +54,14 @@ export class ExpandedFlyout extends FtrService { return nodes[0]; } - async clickOnNodeExpandButton(nodeId: string): Promise { + async clickOnNodeExpandButton( + nodeId: string, + popoverId: string = GRAPH_NODE_EXPAND_POPOVER_TEST_ID + ): Promise { const node = await this.selectNode(nodeId); const expandButton = await node.findByTestSubject(NODE_EXPAND_BUTTON_TEST_ID); await expandButton.click(); - await this.testSubjects.existOrFail(GRAPH_NODE_EXPAND_POPOVER_TEST_ID); + await this.testSubjects.existOrFail(popoverId); } async showActionsByEntity(nodeId: string): Promise { @@ -77,6 +82,12 @@ export class ExpandedFlyout extends FtrService { await this.pageObjects.header.waitUntilLoadingHasFinished(); } + async showEventsOfSameAction(nodeId: string): Promise { + await this.clickOnNodeExpandButton(nodeId, GRAPH_LABEL_EXPAND_POPOVER_TEST_ID); + await this.testSubjects.click(GRAPH_LABEL_EXPAND_POPOVER_SHOW_EVENTS_WITH_THIS_ACTION_ITEM_ID); + await this.pageObjects.header.waitUntilLoadingHasFinished(); + } + async expectFilterTextEquals(filterIdx: number, expected: string): Promise { const filters = await this.filterBar.getFiltersLabel(); expect(filters.length).to.be.greaterThan(filterIdx); diff --git a/x-pack/test/cloud_security_posture_functional/pages/alerts_flyout.ts b/x-pack/test/cloud_security_posture_functional/pages/alerts_flyout.ts index 35f9578929ada..60dd415379c39 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/alerts_flyout.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/alerts_flyout.ts @@ -90,6 +90,19 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'actor.entity.id: admin@example.com OR target.entity.id: admin@example.com OR related.entity: admin@example.com' ); + // Show events with the same action + await expandedFlyout.showEventsOfSameAction( + 'a(admin@example.com)-b(projects/your-project-id/roles/customRole)label(google.iam.admin.v1.CreateRole)outcome(success)' + ); + await expandedFlyout.expectFilterTextEquals( + 0, + 'actor.entity.id: admin@example.com OR target.entity.id: admin@example.com OR related.entity: admin@example.com OR event.action: google.iam.admin.v1.CreateRole' + ); + await expandedFlyout.expectFilterPreviewEquals( + 0, + 'actor.entity.id: admin@example.com OR target.entity.id: admin@example.com OR related.entity: admin@example.com OR event.action: google.iam.admin.v1.CreateRole' + ); + // Clear filters await expandedFlyout.clearAllFilters(); diff --git a/x-pack/test/cloud_security_posture_functional/pages/events_flyout.ts b/x-pack/test/cloud_security_posture_functional/pages/events_flyout.ts index 0848307ca26d2..f3449dd8df40f 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/events_flyout.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/events_flyout.ts @@ -82,6 +82,19 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'actor.entity.id: admin@example.com OR target.entity.id: admin@example.com OR related.entity: admin@example.com' ); + // Show events with the same action + await expandedFlyout.showEventsOfSameAction( + 'a(admin@example.com)-b(projects/your-project-id/roles/customRole)label(google.iam.admin.v1.CreateRole)outcome(success)' + ); + await expandedFlyout.expectFilterTextEquals( + 0, + 'actor.entity.id: admin@example.com OR target.entity.id: admin@example.com OR related.entity: admin@example.com OR event.action: google.iam.admin.v1.CreateRole' + ); + await expandedFlyout.expectFilterPreviewEquals( + 0, + 'actor.entity.id: admin@example.com OR target.entity.id: admin@example.com OR related.entity: admin@example.com OR event.action: google.iam.admin.v1.CreateRole' + ); + // Clear filters await expandedFlyout.clearAllFilters();