From 329020e2d5232a7587fb4f5afdcb01e0d306d974 Mon Sep 17 00:00:00 2001 From: animehart Date: Mon, 21 Oct 2024 09:43:36 -0700 Subject: [PATCH 01/22] initial Push --- .../components/alerts/alerts_preview.tsx | 123 ++++++++++++++++++ .../components/entity_insight.tsx | 8 ++ 2 files changed, 131 insertions(+) create mode 100644 x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx new file mode 100644 index 000000000000..daed3d8f2709 --- /dev/null +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx @@ -0,0 +1,123 @@ +/* + * 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, { useMemo } from 'react'; +import { v4 as uuid } from 'uuid'; +import { css } from '@emotion/react'; +import type { EuiThemeComputed } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle, useEuiTheme } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { DistributionBar } from '@kbn/security-solution-distribution-bar'; +import { ExpandablePanel } from '@kbn/security-solution-common'; +import { getAbbreviatedNumber } from '@kbn/cloud-security-posture-common'; +import { useSummaryChartData } from '../../../detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data'; +import { + getIsAlertsBySeverityData, + getSeverityColor, +} from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; +import { severityAggregations } from '../../../detections/components/alerts_kpis/alerts_summary_charts_panel/aggregations'; + +const ENTITY_ALERT_PREVIEW_COUNT_ID = 'entity-alert-preview-count'; + +const AlertsCount = ({ + alertsTotal, + euiTheme, +}: { + alertsTotal: string | number; + euiTheme: EuiThemeComputed<{}>; +}) => { + return ( + + + + +

{alertsTotal}

+
+
+ + + + + +
+
+ ); +}; + +export const AlertsPreview = ({ + name, + fieldName, + isPreviewMode, +}: { + name: string; + fieldName: 'host.name' | 'user.name'; + isPreviewMode?: boolean; +}) => { + const { euiTheme } = useEuiTheme(); + const uniqueQueryId = useMemo(() => `${ENTITY_ALERT_PREVIEW_COUNT_ID}-${uuid()}`, []); + const entityFilter = useMemo(() => ({ field: fieldName, value: name }), [fieldName, name]); + + const { items, isLoading } = useSummaryChartData({ + aggregations: severityAggregations, + entityFilter, + uniqueQueryId, + signalIndexName: null, + }); + + const data = useMemo(() => (getIsAlertsBySeverityData(items) ? items : []), [items]); + const totalAlerts = data.reduce((accumulator, current) => accumulator + current.value, 0); + const alertStats = useMemo(() => { + return data.map((item) => ({ + key: item.key, + count: item.value, + color: getSeverityColor(item.key), + })); + }, [data]); + + if (!isLoading && items.length === 0) return null; + return ( + + + + ), + }} + data-test-subj={'securitySolutionFlyoutInsightsVulnerabilities'} + > + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx index eee9af194ca3..0070a4df6436 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx @@ -16,6 +16,7 @@ import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks import { hasVulnerabilitiesData } from '@kbn/cloud-security-posture'; import { MisconfigurationsPreview } from './misconfiguration/misconfiguration_preview'; import { VulnerabilitiesPreview } from './vulnerabilities/vulnerabilities_preview'; +import { AlertsPreview } from './alerts/alerts_preview'; export const EntityInsight = ({ name, @@ -60,6 +61,13 @@ export const EntityInsight = ({ const isVulnerabilitiesFindingForHost = hasVulnerabilitiesFindings && fieldName === 'host.name'; + insightContent.push( + <> + + + + ); + if (hasMisconfigurationFindings) insightContent.push( <> From 344a65f31e3c8b652e85761f9c8944474a859892 Mon Sep 17 00:00:00 2001 From: animehart Date: Mon, 21 Oct 2024 22:39:54 -0700 Subject: [PATCH 02/22] reversed array for dist bar --- .../cloud_security_posture/components/alerts/alerts_preview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx index daed3d8f2709..df0e4cea6e58 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx @@ -113,7 +113,7 @@ export const AlertsPreview = ({ - + From c457662e4291bfa9ce5c8c675936d9cb7a94d474 Mon Sep 17 00:00:00 2001 From: animehart Date: Mon, 4 Nov 2024 13:11:12 -0800 Subject: [PATCH 03/22] added test + updated logic to show entity insight component --- .../components/alerts/alerts_preview.test.tsx | 53 +++++++++++++++++++ .../components/alerts/alerts_preview.tsx | 6 +-- .../components/entity_insight.tsx | 3 +- 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx new file mode 100644 index 000000000000..2e1cc026505c --- /dev/null +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx @@ -0,0 +1,53 @@ +/* + * 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. + */ + +/* + * 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 { render } from '@testing-library/react'; +import { useSummaryChartData } from '../../../detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data'; +import { AlertsPreview } from './alerts_preview'; +import { TestProviders } from '../../../common/mock/test_providers'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; + +jest.mock( + '../../../detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data' +); +jest.mock('@kbn/expandable-flyout'); + +describe('AlertsPreview', () => { + (useSummaryChartData as jest.Mock).mockReturnValue({ + items: [{ key: 'low', value: 1, label: 'Low' }], + }); + + const mockOpenLeftPanel = jest.fn(); + + beforeEach(() => { + (useExpandableFlyoutApi as jest.Mock).mockReturnValue({ openLeftPanel: mockOpenLeftPanel }); + (useSummaryChartData as jest.Mock).mockReturnValue({ + items: [{ key: 'low', value: 1, label: 'Low' }], + }); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + + it('renders', () => { + const { getByTestId } = render( + + + + ); + + expect(getByTestId('securitySolutionFlyoutInsightsAlertsTitleText')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx index df0e4cea6e58..c12cf7c4d5bc 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx @@ -12,8 +12,8 @@ import type { EuiThemeComputed } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle, useEuiTheme } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { DistributionBar } from '@kbn/security-solution-distribution-bar'; -import { ExpandablePanel } from '@kbn/security-solution-common'; import { getAbbreviatedNumber } from '@kbn/cloud-security-posture-common'; +import { ExpandablePanel } from '../../../flyout/shared/components/expandable_panel'; import { useSummaryChartData } from '../../../detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data'; import { getIsAlertsBySeverityData, @@ -98,13 +98,13 @@ export const AlertsPreview = ({ `} > ), }} - data-test-subj={'securitySolutionFlyoutInsightsVulnerabilities'} + data-test-subj={'securitySolutionFlyoutInsightsAlerts'} > diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx index 0070a4df6436..28058f9ed6a0 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx @@ -84,7 +84,8 @@ export const EntityInsight = ({ ); return ( <> - {(hasMisconfigurationFindings || + {(insightContent.length !== 0 || + hasMisconfigurationFindings || (isVulnerabilitiesFindingForHost && hasVulnerabilitiesFindings)) && ( <> Date: Wed, 6 Nov 2024 11:30:13 -0800 Subject: [PATCH 04/22] updated logic to only exlude closed alerts --- .../components/alerts/alerts_preview.test.tsx | 23 +++----- .../components/alerts/alerts_preview.tsx | 58 +++++++++---------- .../components/entity_insight.tsx | 46 ++++++++++++--- 3 files changed, 71 insertions(+), 56 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx index 2e1cc026505c..40d6d687f860 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx @@ -5,19 +5,17 @@ * 2.0. */ -/* - * 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 { render } from '@testing-library/react'; -import { useSummaryChartData } from '../../../detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data'; import { AlertsPreview } from './alerts_preview'; import { TestProviders } from '../../../common/mock/test_providers'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import type { ParsedAlertsData } from '../../../overview/components/detection_response/alerts_by_status/types'; + +const mockAlertsData: ParsedAlertsData = { + closed: { total: 1, severities: [{ key: 'low', value: 1, label: 'Low' }] }, + open: { total: 1, severities: [{ key: 'low', value: 1, label: 'Low' }] }, +}; jest.mock( '../../../detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data' @@ -25,17 +23,10 @@ jest.mock( jest.mock('@kbn/expandable-flyout'); describe('AlertsPreview', () => { - (useSummaryChartData as jest.Mock).mockReturnValue({ - items: [{ key: 'low', value: 1, label: 'Low' }], - }); - const mockOpenLeftPanel = jest.fn(); beforeEach(() => { (useExpandableFlyoutApi as jest.Mock).mockReturnValue({ openLeftPanel: mockOpenLeftPanel }); - (useSummaryChartData as jest.Mock).mockReturnValue({ - items: [{ key: 'low', value: 1, label: 'Low' }], - }); }); afterEach(() => { jest.clearAllMocks(); @@ -44,7 +35,7 @@ describe('AlertsPreview', () => { it('renders', () => { const { getByTestId } = render( - + ); diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx index c12cf7c4d5bc..30d492cf460a 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx @@ -5,8 +5,7 @@ * 2.0. */ -import React, { useMemo } from 'react'; -import { v4 as uuid } from 'uuid'; +import React from 'react'; import { css } from '@emotion/react'; import type { EuiThemeComputed } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle, useEuiTheme } from '@elastic/eui'; @@ -14,14 +13,11 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { DistributionBar } from '@kbn/security-solution-distribution-bar'; import { getAbbreviatedNumber } from '@kbn/cloud-security-posture-common'; import { ExpandablePanel } from '../../../flyout/shared/components/expandable_panel'; -import { useSummaryChartData } from '../../../detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data'; -import { - getIsAlertsBySeverityData, - getSeverityColor, -} from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; -import { severityAggregations } from '../../../detections/components/alerts_kpis/alerts_summary_charts_panel/aggregations'; - -const ENTITY_ALERT_PREVIEW_COUNT_ID = 'entity-alert-preview-count'; +import { getSeverityColor } from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; +import type { + AlertsByStatus, + ParsedAlertsData, +} from '../../../overview/components/detection_response/alerts_by_status/types'; const AlertsCount = ({ alertsTotal, @@ -57,36 +53,34 @@ const AlertsCount = ({ }; export const AlertsPreview = ({ - name, - fieldName, + alertsData, + alertsCount, isPreviewMode, }: { - name: string; - fieldName: 'host.name' | 'user.name'; + alertsData: ParsedAlertsData; + alertsCount: number; isPreviewMode?: boolean; }) => { const { euiTheme } = useEuiTheme(); - const uniqueQueryId = useMemo(() => `${ENTITY_ALERT_PREVIEW_COUNT_ID}-${uuid()}`, []); - const entityFilter = useMemo(() => ({ field: fieldName, value: name }), [fieldName, name]); - const { items, isLoading } = useSummaryChartData({ - aggregations: severityAggregations, - entityFilter, - uniqueQueryId, - signalIndexName: null, + const severityMap = new Map(); + + (['open', 'acknowledged'] as AlertsByStatus[]).forEach((status) => { + alertsData?.[status]?.severities.forEach((severity) => { + if (severityMap.has(severity.key)) { + severityMap.set(severity.key, (severityMap?.get(severity.key) || 0) + severity.value); + } else { + severityMap.set(severity.key, severity.value); + } + }); }); - const data = useMemo(() => (getIsAlertsBySeverityData(items) ? items : []), [items]); - const totalAlerts = data.reduce((accumulator, current) => accumulator + current.value, 0); - const alertStats = useMemo(() => { - return data.map((item) => ({ - key: item.key, - count: item.value, - color: getSeverityColor(item.key), - })); - }, [data]); + const alertStats = Array.from(severityMap, ([key, count]) => ({ + key, + count, + color: getSeverityColor(key), + })); - if (!isLoading && items.length === 0) return null; return ( - + diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx index 28058f9ed6a0..093debe1427c 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx @@ -7,7 +7,7 @@ import { EuiAccordion, EuiHorizontalRule, EuiSpacer, EuiTitle, useEuiTheme } from '@elastic/eui'; -import React from 'react'; +import React, { useMemo } from 'react'; import { css } from '@emotion/react'; import { FormattedMessage } from '@kbn/i18n-react'; import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'; @@ -17,6 +17,10 @@ import { hasVulnerabilitiesData } from '@kbn/cloud-security-posture'; import { MisconfigurationsPreview } from './misconfiguration/misconfiguration_preview'; import { VulnerabilitiesPreview } from './vulnerabilities/vulnerabilities_preview'; import { AlertsPreview } from './alerts/alerts_preview'; +import { useGlobalTime } from '../../common/containers/use_global_time'; +import { DETECTION_RESPONSE_ALERTS_BY_STATUS_ID } from '../../overview/components/detection_response/alerts_by_status/types'; +import { useAlertsByStatus } from '../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; +import { useSignalIndex } from '../../detections/containers/detection_engine/alerts/use_signal_index'; export const EntityInsight = ({ name, @@ -61,12 +65,38 @@ export const EntityInsight = ({ const isVulnerabilitiesFindingForHost = hasVulnerabilitiesFindings && fieldName === 'host.name'; - insightContent.push( - <> - - - - ); + const { signalIndexName } = useSignalIndex(); + + const entityFilter = useMemo(() => ({ field: fieldName, value: name }), [fieldName, name]); + + const { to, from } = useGlobalTime(); + + const { items: alertsData } = useAlertsByStatus({ + entityFilter, + signalIndexName, + queryId: DETECTION_RESPONSE_ALERTS_BY_STATUS_ID, + to, + from, + }); + + const alertsOpenCount = alertsData?.open?.total || 0; + + const alertsAcknowledgedCount = alertsData?.acknowledged?.total || 0; + + const alertsCount = alertsOpenCount + alertsAcknowledgedCount; + + if (alertsCount > 0) { + insightContent.push( + <> + + + + ); + } if (hasMisconfigurationFindings) insightContent.push( @@ -84,7 +114,7 @@ export const EntityInsight = ({ ); return ( <> - {(insightContent.length !== 0 || + {(insightContent.length > 0 || hasMisconfigurationFindings || (isVulnerabilitiesFindingForHost && hasVulnerabilitiesFindings)) && ( <> From d49d5ab260f385fd68a3a86ecb59dd4a31209fbd Mon Sep 17 00:00:00 2001 From: animehart Date: Fri, 8 Nov 2024 02:24:19 -0800 Subject: [PATCH 05/22] updated hooks used --- .../common/utils/helpers.ts | 40 ++++++++++++++++++ .../components/alerts/alerts_preview.test.tsx | 39 +++++++++++++++-- .../components/alerts/alerts_preview.tsx | 42 +++++++++++-------- .../components/entity_insight.tsx | 29 ++++--------- 4 files changed, 109 insertions(+), 41 deletions(-) diff --git a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts index 7039c99af6d5..1c520fb85118 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts @@ -62,3 +62,43 @@ export const buildEntityFlyoutPreviewQuery = (field: string, queryValue?: string }, }; }; + +export const buildEntityAlertsQuery = (field: string, queryValue?: string, size?: number) => { + return { + size: size || 0, + _source: false, + fields: [ + 'kibana.alert.rule.uuid', + 'signal.rule.name', + 'signal.rule.severity', + 'kibana.alert.reason', + ], + query: { + bool: { + filter: [ + { + bool: { + must: [], + filter: [ + { + match_phrase: { + [field]: { + query: queryValue, + }, + }, + }, + ], + should: [], + must_not: [], + }, + }, + { + terms: { + 'kibana.alert.workflow_status': ['open', 'acknowledged'], + }, + }, + ], + }, + }, + }; +}; diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx index 40d6d687f860..9a035435ad71 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx @@ -10,11 +10,42 @@ import { render } from '@testing-library/react'; import { AlertsPreview } from './alerts_preview'; import { TestProviders } from '../../../common/mock/test_providers'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import type { ParsedAlertsData } from '../../../overview/components/detection_response/alerts_by_status/types'; +import type { AlertSearchResponse } from '../../../detections/containers/detection_engine/alerts/types'; -const mockAlertsData: ParsedAlertsData = { - closed: { total: 1, severities: [{ key: 'low', value: 1, label: 'Low' }] }, - open: { total: 1, severities: [{ key: 'low', value: 1, label: 'Low' }] }, +const mockAlertsData: AlertSearchResponse = { + took: 0, + timeout: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 2, + relation: 'eq', + }, + max_score: 0, + hits: [ + { + fields: { + 'signal.rule.name': ['Low Alert'], + 'kibana.alert.reason': ['Low Alert Reason'], + 'kibana.alert.rule.uuid': ['Low Alert UUID'], + 'signal.rule.severity': ['low'], + }, + }, + { + fields: { + 'signal.rule.name': ['Medium Alert'], + 'kibana.alert.reason': ['Medium Alert Reason'], + 'kibana.alert.rule.uuid': ['Medium Alert UUID'], + 'signal.rule.severity': ['medium'], + }, + }, + ], + }, }; jest.mock( diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx index 30d492cf460a..2369050ef052 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx @@ -12,12 +12,20 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle, useEuiTheme } import { FormattedMessage } from '@kbn/i18n-react'; import { DistributionBar } from '@kbn/security-solution-distribution-bar'; import { getAbbreviatedNumber } from '@kbn/cloud-security-posture-common'; +import type { AlertSearchResponse } from '../../../detections/containers/detection_engine/alerts/types'; import { ExpandablePanel } from '../../../flyout/shared/components/expandable_panel'; import { getSeverityColor } from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; -import type { - AlertsByStatus, - ParsedAlertsData, -} from '../../../overview/components/detection_response/alerts_by_status/types'; + +interface CspAlertsField { + 'kibana.alert.rule.uuid': string[]; + 'kibana.alert.reason': string[]; + 'signal.rule.name': string[]; + 'signal.rule.severity': string[]; +} + +interface AlertsDetailsFields { + fields: CspAlertsField; +} const AlertsCount = ({ alertsTotal, @@ -57,25 +65,25 @@ export const AlertsPreview = ({ alertsCount, isPreviewMode, }: { - alertsData: ParsedAlertsData; + alertsData: AlertSearchResponse | null; alertsCount: number; isPreviewMode?: boolean; }) => { const { euiTheme } = useEuiTheme(); - const severityMap = new Map(); - - (['open', 'acknowledged'] as AlertsByStatus[]).forEach((status) => { - alertsData?.[status]?.severities.forEach((severity) => { - if (severityMap.has(severity.key)) { - severityMap.set(severity.key, (severityMap?.get(severity.key) || 0) + severity.value); - } else { - severityMap.set(severity.key, severity.value); - } - }); - }); + const resultX = (alertsData?.hits?.hits as AlertsDetailsFields[])?.map( + (item: AlertsDetailsFields) => { + return { fields: item.fields }; + } + ); - const alertStats = Array.from(severityMap, ([key, count]) => ({ + const severities = resultX?.map((item) => item.fields['signal.rule.severity'][0]) || []; + const alertStats = Object.entries( + severities.reduce((acc: Record, item) => { + acc[item] = (acc[item] || 0) + 1; + return acc; + }, {}) + ).map(([key, count]) => ({ key, count, color: getSeverityColor(key), diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx index 093debe1427c..d9a43ee9ce38 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx @@ -7,20 +7,20 @@ import { EuiAccordion, EuiHorizontalRule, EuiSpacer, EuiTitle, useEuiTheme } from '@elastic/eui'; -import React, { useMemo } from 'react'; +import React from 'react'; import { css } from '@emotion/react'; import { FormattedMessage } from '@kbn/i18n-react'; import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'; import { buildEntityFlyoutPreviewQuery } from '@kbn/cloud-security-posture-common'; import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'; import { hasVulnerabilitiesData } from '@kbn/cloud-security-posture'; +import { buildEntityAlertsQuery } from '@kbn/cloud-security-posture-common/utils/helpers'; import { MisconfigurationsPreview } from './misconfiguration/misconfiguration_preview'; import { VulnerabilitiesPreview } from './vulnerabilities/vulnerabilities_preview'; import { AlertsPreview } from './alerts/alerts_preview'; -import { useGlobalTime } from '../../common/containers/use_global_time'; -import { DETECTION_RESPONSE_ALERTS_BY_STATUS_ID } from '../../overview/components/detection_response/alerts_by_status/types'; -import { useAlertsByStatus } from '../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; import { useSignalIndex } from '../../detections/containers/detection_engine/alerts/use_signal_index'; +import { ALERTS_QUERY_NAMES } from '../../detections/containers/detection_engine/alerts/constants'; +import { useQueryAlerts } from '../../detections/containers/detection_engine/alerts/use_query'; export const EntityInsight = ({ name, @@ -67,24 +67,13 @@ export const EntityInsight = ({ const { signalIndexName } = useSignalIndex(); - const entityFilter = useMemo(() => ({ field: fieldName, value: name }), [fieldName, name]); - - const { to, from } = useGlobalTime(); - - const { items: alertsData } = useAlertsByStatus({ - entityFilter, - signalIndexName, - queryId: DETECTION_RESPONSE_ALERTS_BY_STATUS_ID, - to, - from, + const { data: alertsData } = useQueryAlerts({ + query: buildEntityAlertsQuery(fieldName, name, 500), + queryName: ALERTS_QUERY_NAMES.ALERTS_COUNT_BY_STATUS, + indexName: signalIndexName, }); - const alertsOpenCount = alertsData?.open?.total || 0; - - const alertsAcknowledgedCount = alertsData?.acknowledged?.total || 0; - - const alertsCount = alertsOpenCount + alertsAcknowledgedCount; - + const alertsCount = alertsData?.hits?.total.value || 0; if (alertsCount > 0) { insightContent.push( <> From 136b04caa51f6ca2c849db5731b3e4c7f159b733 Mon Sep 17 00:00:00 2001 From: animehart Date: Fri, 8 Nov 2024 13:35:38 -0800 Subject: [PATCH 06/22] reverting logic to previous for preview component --- .../components/alerts/alerts_preview.test.tsx | 68 ++++++++++--------- .../components/alerts/alerts_preview.tsx | 44 +++++------- .../components/entity_insight.tsx | 29 +++++--- 3 files changed, 73 insertions(+), 68 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx index 9a035435ad71..0987268916c6 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx @@ -10,40 +10,22 @@ import { render } from '@testing-library/react'; import { AlertsPreview } from './alerts_preview'; import { TestProviders } from '../../../common/mock/test_providers'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; -import type { AlertSearchResponse } from '../../../detections/containers/detection_engine/alerts/types'; +import type { ParsedAlertsData } from '../../../overview/components/detection_response/alerts_by_status/types'; -const mockAlertsData: AlertSearchResponse = { - took: 0, - timeout: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, +const mockAlertsData: ParsedAlertsData = { + closed: { total: 2, severities: [{ key: 'low', value: 1, label: 'Low' }] }, + open: { + total: 3, + severities: [ + { key: 'low', value: 2, label: 'Low' }, + { key: 'medium', value: 1, label: 'Medium' }, + ], }, - hits: { - total: { - value: 2, - relation: 'eq', - }, - max_score: 0, - hits: [ - { - fields: { - 'signal.rule.name': ['Low Alert'], - 'kibana.alert.reason': ['Low Alert Reason'], - 'kibana.alert.rule.uuid': ['Low Alert UUID'], - 'signal.rule.severity': ['low'], - }, - }, - { - fields: { - 'signal.rule.name': ['Medium Alert'], - 'kibana.alert.reason': ['Medium Alert Reason'], - 'kibana.alert.rule.uuid': ['Medium Alert UUID'], - 'signal.rule.severity': ['medium'], - }, - }, + acknowledged: { + total: 2, + severities: [ + { key: 'low', value: 1, label: 'Low' }, + { key: 'high', value: 1, label: 'High' }, ], }, }; @@ -66,10 +48,30 @@ describe('AlertsPreview', () => { it('renders', () => { const { getByTestId } = render( - + ); expect(getByTestId('securitySolutionFlyoutInsightsAlertsTitleText')).toBeInTheDocument(); }); + + it('renders correct alerts number', () => { + const { getByTestId } = render( + + + + ); + + expect(getByTestId('securitySolutionFlyoutInsightsAlertsCount').textContent).toEqual('5'); + }); + + it('renders correct number distribution bar based on severity', () => { + const { queryAllByTestId } = render( + + + + ); + + expect(queryAllByTestId('undefined__part').length).toEqual(3); + }); }); diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx index 2369050ef052..0a21c485856d 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx @@ -12,20 +12,12 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle, useEuiTheme } import { FormattedMessage } from '@kbn/i18n-react'; import { DistributionBar } from '@kbn/security-solution-distribution-bar'; import { getAbbreviatedNumber } from '@kbn/cloud-security-posture-common'; -import type { AlertSearchResponse } from '../../../detections/containers/detection_engine/alerts/types'; import { ExpandablePanel } from '../../../flyout/shared/components/expandable_panel'; import { getSeverityColor } from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; - -interface CspAlertsField { - 'kibana.alert.rule.uuid': string[]; - 'kibana.alert.reason': string[]; - 'signal.rule.name': string[]; - 'signal.rule.severity': string[]; -} - -interface AlertsDetailsFields { - fields: CspAlertsField; -} +import type { + AlertsByStatus, + ParsedAlertsData, +} from '../../../overview/components/detection_response/alerts_by_status/types'; const AlertsCount = ({ alertsTotal, @@ -39,7 +31,7 @@ const AlertsCount = ({ -

{alertsTotal}

+

{alertsTotal}

@@ -65,25 +57,25 @@ export const AlertsPreview = ({ alertsCount, isPreviewMode, }: { - alertsData: AlertSearchResponse | null; + alertsData: ParsedAlertsData; alertsCount: number; isPreviewMode?: boolean; }) => { const { euiTheme } = useEuiTheme(); - const resultX = (alertsData?.hits?.hits as AlertsDetailsFields[])?.map( - (item: AlertsDetailsFields) => { - return { fields: item.fields }; - } - ); + const severityMap = new Map(); + + (['open', 'acknowledged'] as AlertsByStatus[]).forEach((status) => { + alertsData?.[status]?.severities.forEach((severity) => { + if (severityMap.has(severity.key)) { + severityMap.set(severity.key, (severityMap?.get(severity.key) || 0) + severity.value); + } else { + severityMap.set(severity.key, severity.value); + } + }); + }); - const severities = resultX?.map((item) => item.fields['signal.rule.severity'][0]) || []; - const alertStats = Object.entries( - severities.reduce((acc: Record, item) => { - acc[item] = (acc[item] || 0) + 1; - return acc; - }, {}) - ).map(([key, count]) => ({ + const alertStats = Array.from(severityMap, ([key, count]) => ({ key, count, color: getSeverityColor(key), diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx index d9a43ee9ce38..093debe1427c 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx @@ -7,20 +7,20 @@ import { EuiAccordion, EuiHorizontalRule, EuiSpacer, EuiTitle, useEuiTheme } from '@elastic/eui'; -import React from 'react'; +import React, { useMemo } from 'react'; import { css } from '@emotion/react'; import { FormattedMessage } from '@kbn/i18n-react'; import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'; import { buildEntityFlyoutPreviewQuery } from '@kbn/cloud-security-posture-common'; import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'; import { hasVulnerabilitiesData } from '@kbn/cloud-security-posture'; -import { buildEntityAlertsQuery } from '@kbn/cloud-security-posture-common/utils/helpers'; import { MisconfigurationsPreview } from './misconfiguration/misconfiguration_preview'; import { VulnerabilitiesPreview } from './vulnerabilities/vulnerabilities_preview'; import { AlertsPreview } from './alerts/alerts_preview'; +import { useGlobalTime } from '../../common/containers/use_global_time'; +import { DETECTION_RESPONSE_ALERTS_BY_STATUS_ID } from '../../overview/components/detection_response/alerts_by_status/types'; +import { useAlertsByStatus } from '../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; import { useSignalIndex } from '../../detections/containers/detection_engine/alerts/use_signal_index'; -import { ALERTS_QUERY_NAMES } from '../../detections/containers/detection_engine/alerts/constants'; -import { useQueryAlerts } from '../../detections/containers/detection_engine/alerts/use_query'; export const EntityInsight = ({ name, @@ -67,13 +67,24 @@ export const EntityInsight = ({ const { signalIndexName } = useSignalIndex(); - const { data: alertsData } = useQueryAlerts({ - query: buildEntityAlertsQuery(fieldName, name, 500), - queryName: ALERTS_QUERY_NAMES.ALERTS_COUNT_BY_STATUS, - indexName: signalIndexName, + const entityFilter = useMemo(() => ({ field: fieldName, value: name }), [fieldName, name]); + + const { to, from } = useGlobalTime(); + + const { items: alertsData } = useAlertsByStatus({ + entityFilter, + signalIndexName, + queryId: DETECTION_RESPONSE_ALERTS_BY_STATUS_ID, + to, + from, }); - const alertsCount = alertsData?.hits?.total.value || 0; + const alertsOpenCount = alertsData?.open?.total || 0; + + const alertsAcknowledgedCount = alertsData?.acknowledged?.total || 0; + + const alertsCount = alertsOpenCount + alertsAcknowledgedCount; + if (alertsCount > 0) { insightContent.push( <> From 336299811d1f78ce4f94cccccf29eb601489f048 Mon Sep 17 00:00:00 2001 From: animehart Date: Fri, 8 Nov 2024 13:50:21 -0800 Subject: [PATCH 07/22] fix capital letter issue --- .../components/alerts/alerts_preview.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx index 0a21c485856d..eb531dfc4a0a 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { capitalize } from 'lodash'; import { css } from '@emotion/react'; import type { EuiThemeComputed } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle, useEuiTheme } from '@elastic/eui'; @@ -76,7 +77,7 @@ export const AlertsPreview = ({ }); const alertStats = Array.from(severityMap, ([key, count]) => ({ - key, + key: capitalize(key), count, color: getSeverityColor(key), })); From 45b933859e8e60c348943f5b6ae6ca11574936ae Mon Sep 17 00:00:00 2001 From: animehart Date: Sat, 9 Nov 2024 01:41:31 -0800 Subject: [PATCH 08/22] initial push for alerts table --- .../common/utils/helpers.ts | 16 +- .../components/alerts/alerts_preview.tsx | 145 +++++++++++- .../alerts_findings_details_table.tsx | 224 ++++++++++++++++++ .../csp_details/insights_tab_csp.tsx | 29 ++- .../components/entity_insight.tsx | 15 +- .../misconfiguration_preview.tsx | 10 +- .../vulnerabilities_preview.tsx | 4 + .../host_details_left/index.tsx | 13 +- .../entity_details/host_right/index.tsx | 25 ++ .../left_panel/left_panel_header.tsx | 1 + .../user_details_left/index.tsx | 12 +- .../entity_details/user_details_left/tabs.tsx | 14 +- .../entity_details/user_right/index.tsx | 25 ++ 13 files changed, 515 insertions(+), 18 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx diff --git a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts index 1c520fb85118..a3945243ee28 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts @@ -63,7 +63,13 @@ export const buildEntityFlyoutPreviewQuery = (field: string, queryValue?: string }; }; -export const buildEntityAlertsQuery = (field: string, queryValue?: string, size?: number) => { +export const buildEntityAlertsQuery = ( + field: string, + to: string, + from: string, + queryValue?: string, + size?: number +) => { return { size: size || 0, _source: false, @@ -92,6 +98,14 @@ export const buildEntityAlertsQuery = (field: string, queryValue?: string, size? must_not: [], }, }, + { + range: { + '@timestamp': { + gte: from, + lte: to, + }, + }, + }, { terms: { 'kibana.alert.workflow_status': ['open', 'acknowledged'], diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx index eb531dfc4a0a..e152d793daeb 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx @@ -5,20 +5,41 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import { capitalize } from 'lodash'; import { css } from '@emotion/react'; import type { EuiThemeComputed } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle, useEuiTheme } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { DistributionBar } from '@kbn/security-solution-distribution-bar'; -import { getAbbreviatedNumber } from '@kbn/cloud-security-posture-common'; -import { ExpandablePanel } from '../../../flyout/shared/components/expandable_panel'; -import { getSeverityColor } from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; +import { + buildEntityFlyoutPreviewQuery, + getAbbreviatedNumber, +} from '@kbn/cloud-security-posture-common'; +import { hasVulnerabilitiesData } from '@kbn/cloud-security-posture'; +import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'; +import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import type { AlertsByStatus, ParsedAlertsData, } from '../../../overview/components/detection_response/alerts_by_status/types'; +import { ExpandablePanel } from '../../../flyout/shared/components/expandable_panel'; +import { getSeverityColor } from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; +import type { HostRiskScore, UserRiskScore } from '../../../../common/search_strategy'; +import { + buildHostNamesFilter, + buildUserNamesFilter, + RiskScoreEntity, +} from '../../../../common/search_strategy'; +import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score'; +import { FIRST_RECORD_PAGINATION } from '../../../entity_analytics/common'; +import { HostDetailsPanelKey } from '../../../flyout/entity_details/host_details_left'; +import { + EntityDetailsLeftPanelTab, + CspInsightLeftPanelSubTab, +} from '../../../flyout/entity_details/shared/components/left_panel/left_panel_header'; +import { UserDetailsPanelKey } from '../../../flyout/entity_details/user_details_left'; const AlertsCount = ({ alertsTotal, @@ -56,10 +77,14 @@ const AlertsCount = ({ export const AlertsPreview = ({ alertsData, alertsCount, + fieldName, + name, isPreviewMode, }: { alertsData: ParsedAlertsData; alertsCount: number; + fieldName: string; + name: string; isPreviewMode?: boolean; }) => { const { euiTheme } = useEuiTheme(); @@ -82,9 +107,120 @@ export const AlertsPreview = ({ color: getSeverityColor(key), })); + const { data } = useMisconfigurationPreview({ + query: buildEntityFlyoutPreviewQuery(fieldName, name), + sort: [], + enabled: true, + pageSize: 1, + ignore_unavailable: true, + }); + const isUsingHostName = fieldName === 'host.name'; + const passedFindings = data?.count.passed || 0; + const failedFindings = data?.count.failed || 0; + + const hasMisconfigurationFindings = passedFindings > 0 || failedFindings > 0; + + const { data: vulnerabilitiesData } = useVulnerabilitiesPreview({ + query: buildEntityFlyoutPreviewQuery('host.name', name), + sort: [], + enabled: true, + pageSize: 1, + }); + + const { + CRITICAL = 0, + HIGH = 0, + MEDIUM = 0, + LOW = 0, + NONE = 0, + } = vulnerabilitiesData?.count || {}; + + const hasVulnerabilitiesFindings = hasVulnerabilitiesData({ + critical: CRITICAL, + high: HIGH, + medium: MEDIUM, + low: LOW, + none: NONE, + }); + + const buildFilterQuery = useMemo( + () => (isUsingHostName ? buildHostNamesFilter([name]) : buildUserNamesFilter([name])), + [isUsingHostName, name] + ); + + const riskScoreState = useRiskScore({ + riskEntity: isUsingHostName ? RiskScoreEntity.host : RiskScoreEntity.user, + filterQuery: buildFilterQuery, + onlyLatest: false, + pagination: FIRST_RECORD_PAGINATION, + }); + + const { data: hostRisk } = riskScoreState; + + const riskData = hostRisk?.[0]; + + const isRiskScoreExist = isUsingHostName + ? !!(riskData as HostRiskScore)?.host.risk + : !!(riskData as UserRiskScore)?.user.risk; + + const hasNonClosedAlerts = alertsCount > 0; + + const { openLeftPanel } = useExpandableFlyoutApi(); + + const goToEntityInsightTab = useCallback(() => { + openLeftPanel({ + id: isUsingHostName ? HostDetailsPanelKey : UserDetailsPanelKey, + params: isUsingHostName + ? { + name, + isRiskScoreExist, + hasMisconfigurationFindings, + hasVulnerabilitiesFindings, + hasNonClosedAlerts, + path: { + tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS, + subTab: CspInsightLeftPanelSubTab.ALERTS, + }, + } + : { + user: { name }, + isRiskScoreExist, + hasMisconfigurationFindings, + hasNonClosedAlerts, + path: { + tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS, + subTab: CspInsightLeftPanelSubTab.ALERTS, + }, + }, + }); + }, [ + hasMisconfigurationFindings, + hasNonClosedAlerts, + hasVulnerabilitiesFindings, + isRiskScoreExist, + isUsingHostName, + name, + openLeftPanel, + ]); + const link = useMemo( + () => + !isPreviewMode + ? { + callback: goToEntityInsightTab, + tooltip: ( + + ), + } + : undefined, + [isPreviewMode, goToEntityInsightTab] + ); return ( ), + link: alertsCount > 0 ? link : undefined, }} data-test-subj={'securitySolutionFlyoutInsightsAlerts'} > diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx new file mode 100644 index 000000000000..356da3140fb8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -0,0 +1,224 @@ +/* + * 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, useEffect, useState } from 'react'; +import { capitalize } from 'lodash'; +import type { Criteria, EuiBasicTableColumn } from '@elastic/eui'; +import { EuiSpacer, EuiPanel, EuiText, EuiBasicTable, EuiIcon } from '@elastic/eui'; +import { useMisconfigurationFindings } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_findings'; +import { i18n } from '@kbn/i18n'; +import { buildEntityFlyoutPreviewQuery } from '@kbn/cloud-security-posture-common'; +import { DistributionBar } from '@kbn/security-solution-distribution-bar'; +import { + ENTITY_FLYOUT_EXPAND_MISCONFIGURATION_VIEW_VISITS, + NAV_TO_FINDINGS_BY_HOST_NAME_FRPOM_ENTITY_FLYOUT, + uiMetricService, +} from '@kbn/cloud-security-posture-common/utils/ui_metrics'; +import { METRIC_TYPE } from '@kbn/analytics'; +import { useGetNavigationUrlParams } from '@kbn/cloud-security-posture/src/hooks/use_get_navigation_url_params'; +import { SecurityPageName } from '@kbn/deeplinks-security'; + +import { buildEntityAlertsQuery } from '@kbn/cloud-security-posture-common/utils/helpers'; +import { useGlobalTime } from '../../../common/containers/use_global_time'; +import { SecuritySolutionLinkAnchor } from '../../../common/components/links'; +import { useQueryAlerts } from '../../../detections/containers/detection_engine/alerts/use_query'; +import { ALERTS_QUERY_NAMES } from '../../../detections/containers/detection_engine/alerts/constants'; +import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index'; +import { getSeverityColor } from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; + +interface CspAlertsField { + 'kibana.alert.rule.uuid': string[]; + 'kibana.alert.reason': string[]; + 'signal.rule.name': string[]; + 'signal.rule.severity': string[]; +} + +interface AlertsDetailsFields { + fields: CspAlertsField; +} + +/** + * Insights view displayed in the document details expandable flyout left section + */ +export const AlertsDetailsTable = memo( + ({ fieldName, queryName }: { fieldName: 'host.name' | 'user.name'; queryName: string }) => { + useEffect(() => { + uiMetricService.trackUiMetric( + METRIC_TYPE.COUNT, + ENTITY_FLYOUT_EXPAND_MISCONFIGURATION_VIEW_VISITS + ); + }, []); + + const { data: dataMisconfiguration } = useMisconfigurationFindings({ + query: buildEntityFlyoutPreviewQuery(fieldName, queryName), + sort: [], + enabled: true, + pageSize: 1, + }); + + const passedFindings = dataMisconfiguration?.count.passed || 0; + const failedFindings = dataMisconfiguration?.count.failed || 0; + + const [pageIndex, setPageIndex] = useState(0); + const [pageSize, setPageSize] = useState(10); + + const findingsPagination = (findings: AlertsDetailsFields[]) => { + let pageOfItems; + + if (!pageIndex && !pageSize) { + pageOfItems = findings; + } else { + const startIndex = pageIndex * pageSize; + pageOfItems = findings?.slice( + startIndex, + Math.min(startIndex + pageSize, findings?.length) + ); + } + + return { + pageOfItems, + totalItemCount: findings?.length, + }; + }; + + const getNavUrlParams = useGetNavigationUrlParams(); + + const getFindingsPageUrlFilteredByRuleAndResourceId = (ruleId: string, resourceId: string) => { + return getNavUrlParams({ 'rule.id': ruleId, 'resource.id': resourceId }, 'configurations'); + }; + + const getFindingsPageUrl = (name: string, queryField: 'host.name' | 'user.name') => { + return getNavUrlParams({ [queryField]: name }, 'configurations', ['rule.name']); + }; + + const { to, from } = useGlobalTime(); + const { signalIndexName } = useSignalIndex(); + const { data } = useQueryAlerts({ + query: buildEntityAlertsQuery(fieldName, to, from, queryName, 500), + queryName: ALERTS_QUERY_NAMES.ALERTS_COUNT_BY_STATUS, + indexName: signalIndexName, + }); + + const resultX = (data?.hits?.hits as AlertsDetailsFields[])?.map( + (item: AlertsDetailsFields) => { + return { fields: item.fields }; + } + ); + + const severities = resultX?.map((item) => item.fields['signal.rule.severity'][0]) || []; + const alertStats = Object.entries( + severities.reduce((acc: Record, item) => { + acc[item] = (acc[item] || 0) + 1; + return acc; + }, {}) + ).map(([key, count]) => ({ + key: capitalize(key), + count, + color: getSeverityColor(key), + })); + + const { pageOfItems, totalItemCount } = findingsPagination(resultX || []); + + const pagination = { + pageIndex, + pageSize, + totalItemCount, + pageSizeOptions: [10, 25, 100], + }; + + const onTableChange = ({ page }: Criteria) => { + if (page) { + const { index, size } = page; + setPageIndex(index); + setPageSize(size); + } + }; + + const columns: Array> = [ + { + field: 'fields', + render: (field: CspAlertsField) => ( + {field['signal.rule.name'][0]} + ), + name: i18n.translate( + 'xpack.securitySolution.flyout.left.insights.misconfigurations.table.resultColumnName', + { + defaultMessage: 'Rule', + } + ), + width: '35%', + }, + { + field: 'fields', + render: (field: CspAlertsField) => ( + {field['signal.rule.severity'][0]} + ), + name: i18n.translate( + 'xpack.securitySolution.flyout.left.insights.misconfigurations.table.resultColumnName', + { + defaultMessage: 'Severity', + } + ), + width: '20%', + }, + { + field: 'fields', + render: (field: CspAlertsField) => ( + {field['kibana.alert.reason'][0]} + ), + name: i18n.translate( + 'xpack.securitySolution.flyout.left.insights.misconfigurations.table.ruleColumnName', + { + defaultMessage: 'Reason', + } + ), + width: '40%', + }, + ]; + + return ( + <> + + { + uiMetricService.trackUiMetric( + METRIC_TYPE.CLICK, + NAV_TO_FINDINGS_BY_HOST_NAME_FRPOM_ENTITY_FLYOUT + ); + }} + > + {i18n.translate( + 'xpack.securitySolution.flyout.left.insights.misconfigurations.tableTitle', + { + defaultMessage: 'Misconfigurations ', + } + )} + + + + + + {}} + data-test-subj={'securitySolutionFlyoutMisconfigurationFindingsTable'} + /> + + + ); + } +); + +AlertsDetailsTable.displayName = 'AlertsDetailsTable'; diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/insights_tab_csp.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/insights_tab_csp.tsx index 05421cfa7a20..17e75a7ca88f 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/insights_tab_csp.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/insights_tab_csp.tsx @@ -16,6 +16,7 @@ import { i18n } from '@kbn/i18n'; import { CspInsightLeftPanelSubTab } from '../../../flyout/entity_details/shared/components/left_panel/left_panel_header'; import { MisconfigurationFindingsDetailsTable } from './misconfiguration_findings_details_table'; import { VulnerabilitiesFindingsDetailsTable } from './vulnerabilities_findings_details_table'; +import { AlertsDetailsTable } from './alerts_findings_details_table'; /** * Insights view displayed in the document details expandable flyout left section @@ -26,6 +27,7 @@ interface CspFlyoutPanelProps extends FlyoutPanelProps { path: PanelPath; hasMisconfigurationFindings: boolean; hasVulnerabilitiesFindings: boolean; + hasNonClosedAlerts: boolean; }; } @@ -35,7 +37,8 @@ function isCspFlyoutPanelProps( ): panelLeft is CspFlyoutPanelProps { return ( !!panelLeft?.params?.hasMisconfigurationFindings || - !!panelLeft?.params?.hasVulnerabilitiesFindings + !!panelLeft?.params?.hasVulnerabilitiesFindings || + !!panelLeft?.params?.hasNonClosedAlerts ); } @@ -45,12 +48,14 @@ export const InsightsTabCsp = memo( let hasMisconfigurationFindings = false; let hasVulnerabilitiesFindings = false; + let hasNonClosedAlerts = false; let subTab: string | undefined; // Check if panels.left is of type CspFlyoutPanelProps and extract values if (isCspFlyoutPanelProps(panels.left)) { hasMisconfigurationFindings = panels.left.params.hasMisconfigurationFindings; hasVulnerabilitiesFindings = panels.left.params.hasVulnerabilitiesFindings; + hasNonClosedAlerts = panels.left.params.hasNonClosedAlerts; subTab = panels.left.params.path?.subTab; } @@ -63,6 +68,8 @@ export const InsightsTabCsp = memo( ? CspInsightLeftPanelSubTab.MISCONFIGURATIONS : hasVulnerabilitiesFindings ? CspInsightLeftPanelSubTab.VULNERABILITIES + : hasNonClosedAlerts + ? CspInsightLeftPanelSubTab.ALERTS : ''; }; @@ -71,6 +78,19 @@ export const InsightsTabCsp = memo( const insightsButtons: EuiButtonGroupOptionProps[] = useMemo(() => { const buttons: EuiButtonGroupOptionProps[] = []; + if (panels.left?.params?.hasNonClosedAlerts) { + buttons.push({ + id: CspInsightLeftPanelSubTab.ALERTS, + label: ( + + ), + 'data-test-subj': 'alertsTabDataTestId', + }); + } + if (panels.left?.params?.hasMisconfigurationFindings) { buttons.push({ id: CspInsightLeftPanelSubTab.MISCONFIGURATIONS, @@ -96,9 +116,11 @@ export const InsightsTabCsp = memo( 'data-test-subj': 'vulnerabilitiesTabDataTestId', }); } + return buttons; }, [ panels.left?.params?.hasMisconfigurationFindings, + panels.left?.params?.hasNonClosedAlerts, panels.left?.params?.hasVulnerabilitiesFindings, ]); @@ -130,8 +152,11 @@ export const InsightsTabCsp = memo( {activeInsightsId === CspInsightLeftPanelSubTab.MISCONFIGURATIONS ? ( - ) : ( + ) : activeInsightsId === CspInsightLeftPanelSubTab.VULNERABILITIES ? ( + ) : ( + //
{'ALERTS HERE'}
//AlertsDetailsTable + )} ); diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx index 093debe1427c..11e831635f79 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx @@ -91,6 +91,8 @@ export const EntityInsight = ({ @@ -101,14 +103,23 @@ export const EntityInsight = ({ if (hasMisconfigurationFindings) insightContent.push( <> - + 0} + isPreviewMode={isPreviewMode} + /> ); if (isVulnerabilitiesFindingForHost && hasVulnerabilitiesFindings) insightContent.push( <> - + 0} + /> ); diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/misconfiguration/misconfiguration_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/misconfiguration/misconfiguration_preview.tsx index b133e9db2205..42a5906ce4e3 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/misconfiguration/misconfiguration_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/misconfiguration/misconfiguration_preview.tsx @@ -103,10 +103,12 @@ const MisconfigurationPreviewScore = ({ export const MisconfigurationsPreview = ({ name, fieldName, + hasNonClosedAlerts = false, isPreviewMode, }: { name: string; fieldName: 'host.name' | 'user.name'; + hasNonClosedAlerts?: boolean; isPreviewMode?: boolean; }) => { const { data } = useMisconfigurationPreview({ @@ -180,6 +182,7 @@ export const MisconfigurationsPreview = ({ isRiskScoreExist, hasMisconfigurationFindings, hasVulnerabilitiesFindings, + hasNonClosedAlerts, path: { tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS, subTab: CspInsightLeftPanelSubTab.MISCONFIGURATIONS, @@ -189,11 +192,16 @@ export const MisconfigurationsPreview = ({ user: { name }, isRiskScoreExist, hasMisconfigurationFindings, - path: { tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS }, + hasNonClosedAlerts, + path: { + tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS, + subTab: CspInsightLeftPanelSubTab.MISCONFIGURATIONS, + }, }, }); }, [ hasMisconfigurationFindings, + hasNonClosedAlerts, hasVulnerabilitiesFindings, isRiskScoreExist, isUsingHostName, diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/vulnerabilities/vulnerabilities_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/vulnerabilities/vulnerabilities_preview.tsx index 1caa740662ad..7202d5aeb7ee 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/vulnerabilities/vulnerabilities_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/vulnerabilities/vulnerabilities_preview.tsx @@ -72,9 +72,11 @@ const VulnerabilitiesCount = ({ export const VulnerabilitiesPreview = ({ name, isPreviewMode, + hasNonClosedAlerts = false, }: { name: string; isPreviewMode?: boolean; + hasNonClosedAlerts?: boolean; }) => { useEffect(() => { uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, ENTITY_FLYOUT_WITH_VULNERABILITY_PREVIEW); @@ -132,11 +134,13 @@ export const VulnerabilitiesPreview = ({ isRiskScoreExist, hasMisconfigurationFindings, hasVulnerabilitiesFindings, + hasNonClosedAlerts, path: { tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS, subTab: 'vulnerabilitiesTabId' }, }, }); }, [ hasMisconfigurationFindings, + hasNonClosedAlerts, hasVulnerabilitiesFindings, isRiskScoreExist, name, diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.tsx index 6e5774ba1756..107cb83ddd97 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.tsx @@ -25,6 +25,7 @@ export interface HostDetailsPanelProps extends Record { scopeId: string; hasMisconfigurationFindings?: boolean; hasVulnerabilitiesFindings?: boolean; + hasNonClosedAlerts?: boolean; path?: { tab?: EntityDetailsLeftPanelTab; subTab?: CspInsightLeftPanelSubTab; @@ -43,6 +44,7 @@ export const HostDetailsPanel = ({ path, hasMisconfigurationFindings, hasVulnerabilitiesFindings, + hasNonClosedAlerts, }: HostDetailsPanelProps) => { const [selectedTabId, setSelectedTabId] = useState( path?.tab === EntityDetailsLeftPanelTab.CSP_INSIGHTS @@ -58,11 +60,18 @@ export const HostDetailsPanel = ({ // Determine if the Insights tab should be included const insightsTab = - hasMisconfigurationFindings || hasVulnerabilitiesFindings + hasMisconfigurationFindings || hasVulnerabilitiesFindings || hasNonClosedAlerts ? [getInsightsInputTab({ name, fieldName: 'host.name' })] : []; return [[...riskScoreTab, ...insightsTab], EntityDetailsLeftPanelTab.RISK_INPUTS, () => {}]; - }, [isRiskScoreExist, name, scopeId, hasMisconfigurationFindings, hasVulnerabilitiesFindings]); + }, [ + isRiskScoreExist, + name, + scopeId, + hasMisconfigurationFindings, + hasVulnerabilitiesFindings, + hasNonClosedAlerts, + ]); return ( <> diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx index adc54b58f75c..c29765c052b3 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx @@ -13,6 +13,8 @@ import { buildEntityFlyoutPreviewQuery } from '@kbn/cloud-security-posture-commo import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'; import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'; import { sum } from 'lodash'; +import { DETECTION_RESPONSE_ALERTS_BY_STATUS_ID } from '../../../overview/components/detection_response/alerts_by_status/types'; +import { useAlertsByStatus } from '../../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; import { useRefetchQueryById } from '../../../entity_analytics/api/hooks/use_refetch_query_by_id'; import { RISK_INPUTS_TAB_QUERY_ID } from '../../../entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab'; import type { Refetch } from '../../../common/types'; @@ -35,6 +37,7 @@ import { useObservedHost } from './hooks/use_observed_host'; import { HostDetailsPanelKey } from '../host_details_left'; import { EntityDetailsLeftPanelTab } from '../shared/components/left_panel/left_panel_header'; import { HostPreviewPanelFooter } from '../host_preview/footer'; +import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index'; export interface HostPanelProps extends Record { contextID: string; @@ -119,6 +122,26 @@ export const HostPanel = ({ const hasVulnerabilitiesFindings = sum(Object.values(vulnerabilitiesData?.count || {})) > 0; + const { signalIndexName } = useSignalIndex(); + + const entityFilter = useMemo(() => ({ field: 'host.name', value: hostName }), [hostName]); + + const { items: alertsData } = useAlertsByStatus({ + entityFilter, + signalIndexName, + queryId: `${DETECTION_RESPONSE_ALERTS_BY_STATUS_ID}HOST_NAME_RIGHT`, + to, + from, + }); + + const alertsOpenCount = alertsData?.open?.total || 0; + + const alertsAcknowledgedCount = alertsData?.acknowledged?.total || 0; + + const alertsCount = alertsOpenCount + alertsAcknowledgedCount; + + const hasNonClosedAlerts = alertsCount; + useQueryInspector({ deleteQuery, inspect: inspectRiskScore, @@ -143,6 +166,7 @@ export const HostPanel = ({ path: tab ? { tab } : undefined, hasMisconfigurationFindings, hasVulnerabilitiesFindings, + hasNonClosedAlerts, }, }); }, @@ -154,6 +178,7 @@ export const HostPanel = ({ isRiskScoreExist, hasMisconfigurationFindings, hasVulnerabilitiesFindings, + hasNonClosedAlerts, ] ); diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_header.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_header.tsx index 08623c941ba6..254985b86584 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_header.tsx @@ -28,6 +28,7 @@ export enum EntityDetailsLeftPanelTab { export enum CspInsightLeftPanelSubTab { MISCONFIGURATIONS = 'misconfigurationTabId', VULNERABILITIES = 'vulnerabilitiesTabId', + ALERTS = 'alertsTabId', } export interface PanelHeaderProps { diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx index 8e6cf3a9ee9d..87c9e5abc7af 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx @@ -29,6 +29,7 @@ export interface UserDetailsPanelProps extends Record { path?: PanelPath; scopeId: string; hasMisconfigurationFindings?: boolean; + hasNonClosedAlerts?: boolean; } export interface UserDetailsExpandableFlyoutProps extends FlyoutPanelProps { key: 'user_details'; @@ -42,6 +43,7 @@ export const UserDetailsPanel = ({ path, scopeId, hasMisconfigurationFindings, + hasNonClosedAlerts, }: UserDetailsPanelProps) => { const managedUser = useManagedUser(user.name, user.email); const tabs = useTabs( @@ -49,7 +51,8 @@ export const UserDetailsPanel = ({ user.name, isRiskScoreExist, scopeId, - hasMisconfigurationFindings + hasMisconfigurationFindings, + hasNonClosedAlerts ); const { selectedTabId, setSelectedTabId } = useSelectedTab( @@ -57,7 +60,8 @@ export const UserDetailsPanel = ({ user, tabs, path, - hasMisconfigurationFindings + hasMisconfigurationFindings, + hasNonClosedAlerts ); if (managedUser.isLoading) return ; @@ -83,7 +87,8 @@ const useSelectedTab = ( user: UserParam, tabs: LeftPanelTabsType, path: PanelPath | undefined, - hasMisconfigurationFindings?: boolean + hasMisconfigurationFindings?: boolean, + hasNonClosedAlerts?: boolean ) => { const { openLeftPanel } = useExpandableFlyoutApi(); @@ -101,6 +106,7 @@ const useSelectedTab = ( user, isRiskScoreExist, hasMisconfigurationFindings, + hasNonClosedAlerts, path: { tab: tabId, }, diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx index 6f27b054759f..0c1cdcaa904a 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx @@ -30,7 +30,8 @@ export const useTabs = ( name: string, isRiskScoreExist: boolean, scopeId: string, - hasMisconfigurationFindings?: boolean + hasMisconfigurationFindings?: boolean, + hasNonClosedAlerts?: boolean ): LeftPanelTabsType => useMemo(() => { const tabs: LeftPanelTabsType = []; @@ -55,12 +56,19 @@ export const useTabs = ( tabs.push(getEntraTab(entraManagedUser)); } - if (hasMisconfigurationFindings) { + if (hasMisconfigurationFindings || hasNonClosedAlerts) { tabs.push(getInsightsInputTab({ name, fieldName: 'user.name' })); } return tabs; - }, [hasMisconfigurationFindings, isRiskScoreExist, managedUser, name, scopeId]); + }, [ + hasMisconfigurationFindings, + hasNonClosedAlerts, + isRiskScoreExist, + managedUser, + name, + scopeId, + ]); const getOktaTab = (oktaManagedUser: ManagedUserHit) => ({ id: EntityDetailsLeftPanelTab.OKTA, diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx index 3a60c06e3fae..43a7db8f33bc 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx @@ -33,6 +33,9 @@ import { UserDetailsPanelKey } from '../user_details_left'; import { useObservedUser } from './hooks/use_observed_user'; import { EntityDetailsLeftPanelTab } from '../shared/components/left_panel/left_panel_header'; import { UserPreviewPanelFooter } from '../user_preview/footer'; +import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index'; +import { useAlertsByStatus } from '../../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; +import { DETECTION_RESPONSE_ALERTS_BY_STATUS_ID } from '../../../overview/components/detection_response/alerts_by_status/types'; export interface UserPanelProps extends Record { contextID: string; @@ -111,6 +114,26 @@ export const UserPanel = ({ const hasMisconfigurationFindings = passedFindings > 0 || failedFindings > 0; + const { signalIndexName } = useSignalIndex(); + + const entityFilter = useMemo(() => ({ field: 'user.name', value: userName }), [userName]); + + const { items: alertsData } = useAlertsByStatus({ + entityFilter, + signalIndexName, + queryId: `${DETECTION_RESPONSE_ALERTS_BY_STATUS_ID}USER_NAME_RIGHT`, + to, + from, + }); + + const alertsOpenCount = alertsData?.open?.total || 0; + + const alertsAcknowledgedCount = alertsData?.acknowledged?.total || 0; + + const alertsCount = alertsOpenCount + alertsAcknowledgedCount; + + const hasNonClosedAlerts = alertsCount; + useQueryInspector({ deleteQuery, inspect, @@ -138,6 +161,7 @@ export const UserPanel = ({ }, path: tab ? { tab } : undefined, hasMisconfigurationFindings, + hasNonClosedAlerts, }, }); }, @@ -149,6 +173,7 @@ export const UserPanel = ({ userName, email, hasMisconfigurationFindings, + hasNonClosedAlerts, ] ); const openPanelFirstTab = useCallback( From 61ec46d6f36c05278c4e0d8c10b136111e3e5eb0 Mon Sep 17 00:00:00 2001 From: animehart Date: Tue, 12 Nov 2024 05:23:15 -0800 Subject: [PATCH 09/22] clean up + implemented open flyout from table --- .../common/utils/helpers.ts | 2 + .../hooks/use_misconfiguration_findings.ts | 5 +- .../alerts_findings_details_table.tsx | 105 +++++++++++------- .../entity_details/user_right/index.tsx | 5 +- 4 files changed, 71 insertions(+), 46 deletions(-) diff --git a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts index a3945243ee28..de107de60596 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts @@ -74,6 +74,8 @@ export const buildEntityAlertsQuery = ( size: size || 0, _source: false, fields: [ + '_id', + '_index', 'kibana.alert.rule.uuid', 'signal.rule.name', 'signal.rule.severity', diff --git a/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_findings.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_findings.ts index 40880b132537..9bbaedf587dd 100644 --- a/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_findings.ts +++ b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_findings.ts @@ -40,10 +40,11 @@ export const useMisconfigurationFindings = (options: UseCspOptions) => { params: buildMisconfigurationsFindingsQuery(options, rulesStates!), }) ); - if (!aggregations) throw new Error('expected aggregations to be defined'); + if (!aggregations && options.ignore_unavailable === false) + throw new Error('expected aggregations to be defined'); return { - count: getMisconfigurationAggregationCount(aggregations.count.buckets), + count: getMisconfigurationAggregationCount(aggregations?.count.buckets), rows: hits.hits.map((finding) => ({ result: finding._source?.result, rule: finding?._source?.rule, diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx index 356da3140fb8..2b2ef900f66c 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -5,17 +5,14 @@ * 2.0. */ -import React, { memo, useEffect, useState } from 'react'; +import React, { memo, useCallback, useEffect, useState } from 'react'; import { capitalize } from 'lodash'; import type { Criteria, EuiBasicTableColumn } from '@elastic/eui'; -import { EuiSpacer, EuiPanel, EuiText, EuiBasicTable, EuiIcon } from '@elastic/eui'; -import { useMisconfigurationFindings } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_findings'; +import { EuiSpacer, EuiPanel, EuiText, EuiBasicTable, EuiIcon, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { buildEntityFlyoutPreviewQuery } from '@kbn/cloud-security-posture-common'; import { DistributionBar } from '@kbn/security-solution-distribution-bar'; import { ENTITY_FLYOUT_EXPAND_MISCONFIGURATION_VIEW_VISITS, - NAV_TO_FINDINGS_BY_HOST_NAME_FRPOM_ENTITY_FLYOUT, uiMetricService, } from '@kbn/cloud-security-posture-common/utils/ui_metrics'; import { METRIC_TYPE } from '@kbn/analytics'; @@ -23,18 +20,24 @@ import { useGetNavigationUrlParams } from '@kbn/cloud-security-posture/src/hooks import { SecurityPageName } from '@kbn/deeplinks-security'; import { buildEntityAlertsQuery } from '@kbn/cloud-security-posture-common/utils/helpers'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import { TableId } from '@kbn/securitysolution-data-table'; +import { DocumentDetailsRightPanelKey } from '../../../flyout/document_details/shared/constants/panel_keys'; import { useGlobalTime } from '../../../common/containers/use_global_time'; import { SecuritySolutionLinkAnchor } from '../../../common/components/links'; import { useQueryAlerts } from '../../../detections/containers/detection_engine/alerts/use_query'; import { ALERTS_QUERY_NAMES } from '../../../detections/containers/detection_engine/alerts/constants'; import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index'; import { getSeverityColor } from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; +import { SeverityBadge } from '../../../common/components/severity_badge'; interface CspAlertsField { + _id: string[]; + _index: string[]; 'kibana.alert.rule.uuid': string[]; 'kibana.alert.reason': string[]; 'signal.rule.name': string[]; - 'signal.rule.severity': string[]; + 'signal.rule.severity': Array<'low' | 'medium' | 'high' | 'critical'>; } interface AlertsDetailsFields { @@ -53,16 +56,6 @@ export const AlertsDetailsTable = memo( ); }, []); - const { data: dataMisconfiguration } = useMisconfigurationFindings({ - query: buildEntityFlyoutPreviewQuery(fieldName, queryName), - sort: [], - enabled: true, - pageSize: 1, - }); - - const passedFindings = dataMisconfiguration?.count.passed || 0; - const failedFindings = dataMisconfiguration?.count.failed || 0; - const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(10); @@ -87,12 +80,8 @@ export const AlertsDetailsTable = memo( const getNavUrlParams = useGetNavigationUrlParams(); - const getFindingsPageUrlFilteredByRuleAndResourceId = (ruleId: string, resourceId: string) => { - return getNavUrlParams({ 'rule.id': ruleId, 'resource.id': resourceId }, 'configurations'); - }; - - const getFindingsPageUrl = (name: string, queryField: 'host.name' | 'user.name') => { - return getNavUrlParams({ [queryField]: name }, 'configurations', ['rule.name']); + const getAlertsPageUrl = (name: string, queryField: 'host.name' | 'user.name') => { + return getNavUrlParams({ [queryField]: name }); }; const { to, from } = useGlobalTime(); @@ -103,15 +92,16 @@ export const AlertsDetailsTable = memo( indexName: signalIndexName, }); - const resultX = (data?.hits?.hits as AlertsDetailsFields[])?.map( + const alertDataResults = (data?.hits?.hits as AlertsDetailsFields[])?.map( (item: AlertsDetailsFields) => { return { fields: item.fields }; } ); - const severities = resultX?.map((item) => item.fields['signal.rule.severity'][0]) || []; + const severitiesMap = + alertDataResults?.map((item) => item.fields['signal.rule.severity'][0]) || []; const alertStats = Object.entries( - severities.reduce((acc: Record, item) => { + severitiesMap.reduce((acc: Record, item) => { acc[item] = (acc[item] || 0) + 1; return acc; }, {}) @@ -121,7 +111,7 @@ export const AlertsDetailsTable = memo( color: getSeverityColor(key), })); - const { pageOfItems, totalItemCount } = findingsPagination(resultX || []); + const { pageOfItems, totalItemCount } = findingsPagination(alertDataResults || []); const pagination = { pageIndex, @@ -138,7 +128,42 @@ export const AlertsDetailsTable = memo( } }; + const { openFlyout } = useExpandableFlyoutApi(); + + const handleOnEventAlertDetailPanelOpened = useCallback( + (eventId: string, indexName: string, tableId: string) => { + openFlyout({ + right: { + id: DocumentDetailsRightPanelKey, + path: { tab: 'overview' }, + params: { + id: eventId, + indexName, + scopeId: tableId, + }, + }, + }); + }, + [openFlyout] + ); + + const tableId = fieldName === 'host.name' ? TableId.hostsPageEvents : TableId.usersPageEvents; + const columns: Array> = [ + { + field: 'fields', + name: '', + width: '5%', + render: (field: CspAlertsField) => ( + + handleOnEventAlertDetailPanelOpened(field._id[0], field._index[0], tableId) + } + > + + + ), + }, { field: 'fields', render: (field: CspAlertsField) => ( @@ -155,7 +180,12 @@ export const AlertsDetailsTable = memo( { field: 'fields', render: (field: CspAlertsField) => ( - {field['signal.rule.severity'][0]} + + + ), name: i18n.translate( 'xpack.securitySolution.flyout.left.insights.misconfigurations.table.resultColumnName', @@ -184,23 +214,15 @@ export const AlertsDetailsTable = memo( <> { - uiMetricService.trackUiMetric( - METRIC_TYPE.CLICK, - NAV_TO_FINDINGS_BY_HOST_NAME_FRPOM_ENTITY_FLYOUT - ); - }} + onClick={() => {}} > - {i18n.translate( - 'xpack.securitySolution.flyout.left.insights.misconfigurations.tableTitle', - { - defaultMessage: 'Misconfigurations ', - } - )} + {i18n.translate('xpack.securitySolution.flyout.left.insights.alerts.tableTitle', { + defaultMessage: 'Alerts ', + })} @@ -212,7 +234,6 @@ export const AlertsDetailsTable = memo( columns={columns} pagination={pagination} onChange={onTableChange} - // onChange={() => {}} data-test-subj={'securitySolutionFlyoutMisconfigurationFindingsTable'} /> diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx index 43a7db8f33bc..a3f4a8cd3db3 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx @@ -132,7 +132,7 @@ export const UserPanel = ({ const alertsCount = alertsOpenCount + alertsAcknowledgedCount; - const hasNonClosedAlerts = alertsCount; + const hasNonClosedAlerts = alertsCount > 0; useQueryInspector({ deleteQuery, @@ -215,7 +215,8 @@ export const UserPanel = ({ <> From 59dceab75208b041bf0cf0b3c16d3947e0425ae5 Mon Sep 17 00:00:00 2001 From: animehart Date: Tue, 12 Nov 2024 11:45:48 -0800 Subject: [PATCH 10/22] update open flyout logic --- .../alerts_findings_details_table.tsx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx index 2b2ef900f66c..8e4dd33d4ae4 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -22,7 +22,7 @@ import { SecurityPageName } from '@kbn/deeplinks-security'; import { buildEntityAlertsQuery } from '@kbn/cloud-security-posture-common/utils/helpers'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import { TableId } from '@kbn/securitysolution-data-table'; -import { DocumentDetailsRightPanelKey } from '../../../flyout/document_details/shared/constants/panel_keys'; +import { DocumentDetailsPreviewPanelKey } from '../../../flyout/document_details/shared/constants/panel_keys'; import { useGlobalTime } from '../../../common/containers/use_global_time'; import { SecuritySolutionLinkAnchor } from '../../../common/components/links'; import { useQueryAlerts } from '../../../detections/containers/detection_engine/alerts/use_query'; @@ -30,6 +30,7 @@ import { ALERTS_QUERY_NAMES } from '../../../detections/containers/detection_eng import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index'; import { getSeverityColor } from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; import { SeverityBadge } from '../../../common/components/severity_badge'; +import { ALERT_PREVIEW_BANNER } from '../../../flyout/document_details/preview/constants'; interface CspAlertsField { _id: string[]; @@ -128,26 +129,25 @@ export const AlertsDetailsTable = memo( } }; - const { openFlyout } = useExpandableFlyoutApi(); + const { openPreviewPanel } = useExpandableFlyoutApi(); const handleOnEventAlertDetailPanelOpened = useCallback( (eventId: string, indexName: string, tableId: string) => { - openFlyout({ - right: { - id: DocumentDetailsRightPanelKey, - path: { tab: 'overview' }, - params: { - id: eventId, - indexName, - scopeId: tableId, - }, + openPreviewPanel({ + id: DocumentDetailsPreviewPanelKey, + params: { + id: eventId, + indexName, + scopeId: tableId, + isPreviewMode: true, + banner: ALERT_PREVIEW_BANNER, }, }); }, - [openFlyout] + [openPreviewPanel] ); - const tableId = fieldName === 'host.name' ? TableId.hostsPageEvents : TableId.usersPageEvents; + const tableId = TableId.alertsOnRuleDetailsPage; const columns: Array> = [ { From 41f1833d8919e9d9ead31a09cfdef6daf44b9c70 Mon Sep 17 00:00:00 2001 From: animehart Date: Wed, 13 Nov 2024 06:48:03 -0800 Subject: [PATCH 11/22] changed fields to use --- .../kbn-cloud-security-posture/common/utils/helpers.ts | 4 ++-- .../csp_details/alerts_findings_details_table.tsx | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts index de107de60596..7a7a8e37652a 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts @@ -77,9 +77,9 @@ export const buildEntityAlertsQuery = ( '_id', '_index', 'kibana.alert.rule.uuid', - 'signal.rule.name', - 'signal.rule.severity', 'kibana.alert.reason', + 'kibana.alert.severity', + 'kibana.alert.rule.name', ], query: { bool: { diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx index 8e4dd33d4ae4..d09b9611ba68 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -37,8 +37,8 @@ interface CspAlertsField { _index: string[]; 'kibana.alert.rule.uuid': string[]; 'kibana.alert.reason': string[]; - 'signal.rule.name': string[]; - 'signal.rule.severity': Array<'low' | 'medium' | 'high' | 'critical'>; + 'kibana.alert.severity': Array<'low' | 'medium' | 'high' | 'critical'>; + 'kibana.alert.rule.name': string[]; } interface AlertsDetailsFields { @@ -100,7 +100,7 @@ export const AlertsDetailsTable = memo( ); const severitiesMap = - alertDataResults?.map((item) => item.fields['signal.rule.severity'][0]) || []; + alertDataResults?.map((item) => item.fields['kibana.alert.severity'][0]) || []; const alertStats = Object.entries( severitiesMap.reduce((acc: Record, item) => { acc[item] = (acc[item] || 0) + 1; @@ -167,7 +167,7 @@ export const AlertsDetailsTable = memo( { field: 'fields', render: (field: CspAlertsField) => ( - {field['signal.rule.name'][0]} + {field['kibana.alert.rule.name'][0]} ), name: i18n.translate( 'xpack.securitySolution.flyout.left.insights.misconfigurations.table.resultColumnName', @@ -182,7 +182,7 @@ export const AlertsDetailsTable = memo( render: (field: CspAlertsField) => ( From 30ae6d81491edd487b5312814bfdc0968ef1db9a Mon Sep 17 00:00:00 2001 From: animehart Date: Wed, 13 Nov 2024 12:38:57 -0800 Subject: [PATCH 12/22] fix quick checks --- .../csp_details/alerts_findings_details_table.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx index 5ecdee18846c..0bf68f97823e 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -168,7 +168,7 @@ export const AlertsDetailsTable = memo( {field['kibana.alert.rule.name'][0]} ), name: i18n.translate( - 'xpack.securitySolution.flyout.left.insights.misconfigurations.table.resultColumnName', + 'xpack.securitySolution.flyout.left.insights.alerts.table.ruleNameColumnName', { defaultMessage: 'Rule', } @@ -186,7 +186,7 @@ export const AlertsDetailsTable = memo( ), name: i18n.translate( - 'xpack.securitySolution.flyout.left.insights.misconfigurations.table.resultColumnName', + 'xpack.securitySolution.flyout.left.insights.alerts.table.severityColumnName', { defaultMessage: 'Severity', } @@ -199,7 +199,7 @@ export const AlertsDetailsTable = memo( {field['kibana.alert.reason'][0]} ), name: i18n.translate( - 'xpack.securitySolution.flyout.left.insights.misconfigurations.table.ruleColumnName', + 'xpack.securitySolution.flyout.left.insights.alerts.table.reasonColumnName', { defaultMessage: 'Reason', } From 41bdc3bb535f2393e73d81040cfae5ac4d5d6d19 Mon Sep 17 00:00:00 2001 From: animehart Date: Wed, 13 Nov 2024 14:37:31 -0800 Subject: [PATCH 13/22] added navigate to alerts page --- .../alerts_findings_details_table.tsx | 59 +++++++++++++------ ...se_navigate_to_alerts_page_with_filters.ts | 3 +- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx index 0bf68f97823e..f38a1a31ce5b 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -8,15 +8,7 @@ import React, { memo, useCallback, useEffect, useState } from 'react'; import { capitalize } from 'lodash'; import type { Criteria, EuiBasicTableColumn } from '@elastic/eui'; -import { - EuiSpacer, - EuiPanel, - EuiText, - EuiBasicTable, - EuiIcon, - EuiLink, - EuiTitle, -} from '@elastic/eui'; +import { EuiSpacer, EuiPanel, EuiText, EuiBasicTable, EuiIcon, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DistributionBar } from '@kbn/security-solution-distribution-bar'; import { @@ -27,6 +19,12 @@ import { METRIC_TYPE } from '@kbn/analytics'; import { buildEntityAlertsQuery } from '@kbn/cloud-security-posture-common/utils/helpers'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import { TableId } from '@kbn/securitysolution-data-table'; +import { + OPEN_IN_ALERTS_TITLE_HOSTNAME, + OPEN_IN_ALERTS_TITLE_STATUS, + OPEN_IN_ALERTS_TITLE_USERNAME, +} from '../../../overview/components/detection_response/translations'; +import { useNavigateToAlertsPageWithFilters } from '../../../common/hooks/use_navigate_to_alerts_page_with_filters'; import { DocumentDetailsPreviewPanelKey } from '../../../flyout/document_details/shared/constants/panel_keys'; import { useGlobalTime } from '../../../common/containers/use_global_time'; import { useQueryAlerts } from '../../../detections/containers/detection_engine/alerts/use_query'; @@ -35,6 +33,7 @@ import { useSignalIndex } from '../../../detections/containers/detection_engine/ import { getSeverityColor } from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; import { SeverityBadge } from '../../../common/components/severity_badge'; import { ALERT_PREVIEW_BANNER } from '../../../flyout/document_details/preview/constants'; +import { FILTER_OPEN, FILTER_ACKNOWLEDGED } from '../../../../common/types'; interface CspAlertsField { _id: string[]; @@ -64,22 +63,19 @@ export const AlertsDetailsTable = memo( const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(10); - const findingsPagination = (findings: AlertsDetailsFields[]) => { + const alertsPagination = (alerts: AlertsDetailsFields[]) => { let pageOfItems; if (!pageIndex && !pageSize) { - pageOfItems = findings; + pageOfItems = alerts; } else { const startIndex = pageIndex * pageSize; - pageOfItems = findings?.slice( - startIndex, - Math.min(startIndex + pageSize, findings?.length) - ); + pageOfItems = alerts?.slice(startIndex, Math.min(startIndex + pageSize, alerts?.length)); } return { pageOfItems, - totalItemCount: findings?.length, + totalItemCount: alerts?.length, }; }; @@ -110,7 +106,7 @@ export const AlertsDetailsTable = memo( color: getSeverityColor(key), })); - const { pageOfItems, totalItemCount } = findingsPagination(alertDataResults || []); + const { pageOfItems, totalItemCount } = alertsPagination(alertDataResults || []); const pagination = { pageIndex, @@ -208,16 +204,41 @@ export const AlertsDetailsTable = memo( }, ]; + const openAlertsPageWithFilters = useNavigateToAlertsPageWithFilters(); + + const openHostInAlerts = useCallback( + () => + openAlertsPageWithFilters( + [ + { + title: + fieldName === 'host.name' + ? OPEN_IN_ALERTS_TITLE_HOSTNAME + : OPEN_IN_ALERTS_TITLE_USERNAME, + selectedOptions: [queryName], + fieldName, + }, + { + title: OPEN_IN_ALERTS_TITLE_STATUS, + selectedOptions: [FILTER_OPEN, FILTER_ACKNOWLEDGED], + fieldName: 'kibana.alert.workflow_status', + }, + ], + true + ), + [fieldName, openAlertsPageWithFilters, queryName] + ); + return ( <> - + openHostInAlerts()}>

{i18n.translate('xpack.securitySolution.flyout.left.insights.alerts.tableTitle', { defaultMessage: 'Alerts ', })}

-
+ diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.ts b/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.ts index fffa65797b3f..037bac32d8c9 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.ts @@ -16,7 +16,7 @@ import { URL_PARAM_KEY } from './use_url_state'; export const useNavigateToAlertsPageWithFilters = () => { const { navigateTo } = useNavigation(); - return (filterItems: FilterControlConfig | FilterControlConfig[]) => { + return (filterItems: FilterControlConfig | FilterControlConfig[], openInNewTab = false) => { const urlFilterParams = encode( formatPageFilterSearchParam(Array.isArray(filterItems) ? filterItems : [filterItems]) ); @@ -24,6 +24,7 @@ export const useNavigateToAlertsPageWithFilters = () => { navigateTo({ deepLinkId: SecurityPageName.alerts, path: `?${URL_PARAM_KEY.pageFilter}=${urlFilterParams}`, + openInNewTab, }); }; }; From 0264507f5ffcbb0119392b9002077af81367bf79 Mon Sep 17 00:00:00 2001 From: animehart Date: Wed, 13 Nov 2024 16:27:51 -0800 Subject: [PATCH 14/22] more clean up + added unit test for helper --- .../common/utils/helpers.test.ts | 75 +++++++++++++++++++ .../components/alerts/alerts_preview.test.tsx | 3 - .../alerts_findings_details_table.tsx | 26 +++---- ...vigate_to_alerts_page_with_filters.test.ts | 2 + 4 files changed, 90 insertions(+), 16 deletions(-) diff --git a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts index 0248cdf9b6e3..01a5b2b24400 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts @@ -10,6 +10,7 @@ import { defaultErrorMessage, buildMutedRulesFilter, buildEntityFlyoutPreviewQuery, + buildEntityAlertsQuery, } from './helpers'; const fallbackMessage = 'thisIsAFallBackMessage'; @@ -182,4 +183,78 @@ describe('test helper methods', () => { expect(buildEntityFlyoutPreviewQuery(field)).toEqual(expectedQuery); }); }); + + describe('buildEntityAlertsQuery', () => { + const getExpectedAlertsQuery = (size?: number) => { + return { + size: size || 0, + _source: false, + fields: [ + '_id', + '_index', + 'kibana.alert.rule.uuid', + 'kibana.alert.reason', + 'kibana.alert.severity', + 'kibana.alert.rule.name', + ], + query: { + bool: { + filter: [ + { + bool: { + must: [], + filter: [ + { + match_phrase: { + 'host.name': { + query: 'exampleHost', + }, + }, + }, + ], + should: [], + must_not: [], + }, + }, + { + range: { + '@timestamp': { + gte: 'Today', + lte: 'Tomorrow', + }, + }, + }, + { + terms: { + 'kibana.alert.workflow_status': ['open', 'acknowledged'], + }, + }, + ], + }, + }, + }; + }; + + it('should return the correct query when given all params', () => { + const field = 'host.name'; + const query = 'exampleHost'; + const to = 'Tomorrow'; + const from = 'Today'; + const size = 100; + + expect(buildEntityAlertsQuery(field, to, from, query, size)).toEqual( + getExpectedAlertsQuery(size) + ); + }); + + it('should return the correct query when not given size', () => { + const field = 'host.name'; + const query = 'exampleHost'; + const to = 'Tomorrow'; + const from = 'Today'; + const size = undefined; + + expect(buildEntityAlertsQuery(field, to, from, query)).toEqual(getExpectedAlertsQuery(size)); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx index a0b290e4ad79..fff9450b6a1c 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx @@ -36,9 +36,6 @@ const mockAlertsData: ParsedAlertsData = { jest.mock('@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'); jest.mock('@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'); jest.mock('../../../entity_analytics/api/hooks/use_risk_score'); -// jest.mock( -// '../../../detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data' -// ); jest.mock('@kbn/expandable-flyout'); describe('AlertsPreview', () => { diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx index f38a1a31ce5b..cd292a090882 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -35,22 +35,21 @@ import { SeverityBadge } from '../../../common/components/severity_badge'; import { ALERT_PREVIEW_BANNER } from '../../../flyout/document_details/preview/constants'; import { FILTER_OPEN, FILTER_ACKNOWLEDGED } from '../../../../common/types'; -interface CspAlertsField { +type AlertSeverity = 'low' | 'medium' | 'high' | 'critical'; + +interface ContextualFlyoutAlertsField { _id: string[]; _index: string[]; 'kibana.alert.rule.uuid': string[]; 'kibana.alert.reason': string[]; - 'kibana.alert.severity': Array<'low' | 'medium' | 'high' | 'critical'>; + 'kibana.alert.severity': AlertSeverity[]; 'kibana.alert.rule.name': string[]; } interface AlertsDetailsFields { - fields: CspAlertsField; + fields: ContextualFlyoutAlertsField; } -/** - * Insights view displayed in the document details expandable flyout left section - */ export const AlertsDetailsTable = memo( ({ fieldName, queryName }: { fieldName: 'host.name' | 'user.name'; queryName: string }) => { useEffect(() => { @@ -148,7 +147,7 @@ export const AlertsDetailsTable = memo( field: 'fields', name: '', width: '5%', - render: (field: CspAlertsField) => ( + render: (field: ContextualFlyoutAlertsField) => ( handleOnEventAlertDetailPanelOpened(field._id[0], field._index[0], tableId) @@ -160,7 +159,7 @@ export const AlertsDetailsTable = memo( }, { field: 'fields', - render: (field: CspAlertsField) => ( + render: (field: ContextualFlyoutAlertsField) => ( {field['kibana.alert.rule.name'][0]} ), name: i18n.translate( @@ -173,10 +172,10 @@ export const AlertsDetailsTable = memo( }, { field: 'fields', - render: (field: CspAlertsField) => ( + render: (field: ContextualFlyoutAlertsField) => ( @@ -191,7 +190,7 @@ export const AlertsDetailsTable = memo( }, { field: 'fields', - render: (field: CspAlertsField) => ( + render: (field: ContextualFlyoutAlertsField) => ( {field['kibana.alert.reason'][0]} ), name: i18n.translate( @@ -206,7 +205,7 @@ export const AlertsDetailsTable = memo( const openAlertsPageWithFilters = useNavigateToAlertsPageWithFilters(); - const openHostInAlerts = useCallback( + const openAlertsInAlertsPage = useCallback( () => openAlertsPageWithFilters( [ @@ -232,11 +231,12 @@ export const AlertsDetailsTable = memo( return ( <> - openHostInAlerts()}> + openAlertsInAlertsPage()}>

{i18n.translate('xpack.securitySolution.flyout.left.insights.alerts.tableTitle', { defaultMessage: 'Alerts ', })} +

diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.test.ts b/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.test.ts index 6a957318f278..3bfc0c56e81f 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.test.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.test.ts @@ -32,6 +32,7 @@ describe('useNavigateToAlertsPageWithFilters', () => { expect(mockNavigateTo).toHaveBeenCalledWith({ deepLinkId: SecurityPageName.alerts, path: "?pageFilters=!((exclude:!f,existsSelected:!f,fieldName:'test field',hideActionBar:!f,selectedOptions:!('test value'),title:'test filter'))", + openInNewTab: false, }); }); @@ -63,6 +64,7 @@ describe('useNavigateToAlertsPageWithFilters', () => { expect(mockNavigateTo).toHaveBeenCalledWith({ deepLinkId: SecurityPageName.alerts, path: "?pageFilters=!((exclude:!f,existsSelected:!f,fieldName:'test field 1',hideActionBar:!f,selectedOptions:!('test value 1'),title:'test filter 1'),(exclude:!t,existsSelected:!t,fieldName:'test field 2',hideActionBar:!t,selectedOptions:!('test value 2'),title:'test filter 2'))", + openInNewTab: false, }); }); From 5beeed813354ff203c542ca2c43cd36e2d2d3c86 Mon Sep 17 00:00:00 2001 From: animehart Date: Wed, 13 Nov 2024 18:30:37 -0800 Subject: [PATCH 15/22] more improvements --- .../kbn-cloud-security-posture/common/utils/helpers.ts | 4 +++- x-pack/plugins/cloud_security_posture/kibana.jsonc | 3 ++- .../components/alerts/alerts_preview.tsx | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts index 7a7a8e37652a..159320805324 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts @@ -5,7 +5,9 @@ * 2.0. */ import { QueryDslQueryContainer } from '@kbn/data-views-plugin/common/types'; + import { i18n } from '@kbn/i18n'; +import { FILTER_OPEN, FILTER_ACKNOWLEDGED } from '@kbn/securitysolution-data-table/common/types'; import type { CspBenchmarkRulesStates } from '../schema/rules/latest'; export const defaultErrorMessage = i18n.translate('xpack.csp.common.utils.helpers.unknownError', { @@ -110,7 +112,7 @@ export const buildEntityAlertsQuery = ( }, { terms: { - 'kibana.alert.workflow_status': ['open', 'acknowledged'], + 'kibana.alert.workflow_status': [FILTER_OPEN, FILTER_ACKNOWLEDGED], }, }, ], diff --git a/x-pack/plugins/cloud_security_posture/kibana.jsonc b/x-pack/plugins/cloud_security_posture/kibana.jsonc index d43f37fd7048..e9c47b43f38e 100644 --- a/x-pack/plugins/cloud_security_posture/kibana.jsonc +++ b/x-pack/plugins/cloud_security_posture/kibana.jsonc @@ -30,7 +30,8 @@ "share", "kibanaUtils", "alerting", - "spaces" + "spaces", + "timelines" ], "optionalPlugins": [ "usageCollection" diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx index 40a2ac3b66df..c832f12c93f7 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx @@ -209,8 +209,8 @@ export const AlertsPreview = ({ callback: goToEntityInsightTab, tooltip: ( ), } From 15d5a57687430a68639aefd36510b6bbed537486 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 14 Nov 2024 02:40:47 +0000 Subject: [PATCH 16/22] [CI] Auto-commit changed files from 'node scripts/notice' --- x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json b/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json index ebec9929559f..a60eef58948f 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json +++ b/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json @@ -21,5 +21,6 @@ "@kbn/analytics", "@kbn/usage-collection-plugin", "@kbn/es-query", + "@kbn/securitysolution-data-table", ] } From edfc43302b22cd9f3d26dc3b391c209e4267883f Mon Sep 17 00:00:00 2001 From: animehart Date: Wed, 13 Nov 2024 21:34:45 -0800 Subject: [PATCH 17/22] reverting some changes --- .../kbn-cloud-security-posture/common/utils/helpers.ts | 3 +-- x-pack/plugins/cloud_security_posture/kibana.jsonc | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts index 159320805324..d9b43642000e 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts @@ -7,7 +7,6 @@ import { QueryDslQueryContainer } from '@kbn/data-views-plugin/common/types'; import { i18n } from '@kbn/i18n'; -import { FILTER_OPEN, FILTER_ACKNOWLEDGED } from '@kbn/securitysolution-data-table/common/types'; import type { CspBenchmarkRulesStates } from '../schema/rules/latest'; export const defaultErrorMessage = i18n.translate('xpack.csp.common.utils.helpers.unknownError', { @@ -112,7 +111,7 @@ export const buildEntityAlertsQuery = ( }, { terms: { - 'kibana.alert.workflow_status': [FILTER_OPEN, FILTER_ACKNOWLEDGED], + 'kibana.alert.workflow_status': ['open', 'acknowledged'], }, }, ], diff --git a/x-pack/plugins/cloud_security_posture/kibana.jsonc b/x-pack/plugins/cloud_security_posture/kibana.jsonc index e9c47b43f38e..d43f37fd7048 100644 --- a/x-pack/plugins/cloud_security_posture/kibana.jsonc +++ b/x-pack/plugins/cloud_security_posture/kibana.jsonc @@ -30,8 +30,7 @@ "share", "kibanaUtils", "alerting", - "spaces", - "timelines" + "spaces" ], "optionalPlugins": [ "usageCollection" From d011db857c934bd93d5638747afaf89f9b6f7de6 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 14 Nov 2024 05:48:27 +0000 Subject: [PATCH 18/22] [CI] Auto-commit changed files from 'node scripts/notice' --- x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json b/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json index a60eef58948f..ebec9929559f 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json +++ b/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json @@ -21,6 +21,5 @@ "@kbn/analytics", "@kbn/usage-collection-plugin", "@kbn/es-query", - "@kbn/securitysolution-data-table", ] } From 68d5c11cc38dc1dc4348743f61236168c16f3ddb Mon Sep 17 00:00:00 2001 From: animehart Date: Wed, 13 Nov 2024 22:31:11 -0800 Subject: [PATCH 19/22] update q name --- .../components/csp_details/alerts_findings_details_table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx index cd292a090882..0a890bc6d4fe 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -82,7 +82,7 @@ export const AlertsDetailsTable = memo( const { signalIndexName } = useSignalIndex(); const { data } = useQueryAlerts({ query: buildEntityAlertsQuery(fieldName, to, from, queryName, 500), - queryName: ALERTS_QUERY_NAMES.ALERTS_COUNT_BY_STATUS, + queryName: ALERTS_QUERY_NAMES.BY_RULE_BY_STATUS, indexName: signalIndexName, }); From 4e396714151f5b21f6f7c61443878f4f6670728f Mon Sep 17 00:00:00 2001 From: animehart Date: Wed, 13 Nov 2024 23:49:40 -0800 Subject: [PATCH 20/22] clean up --- .../alerts_findings_details_table.tsx | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx index 0a890bc6d4fe..fa9ebc2fc285 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -37,7 +37,7 @@ import { FILTER_OPEN, FILTER_ACKNOWLEDGED } from '../../../../common/types'; type AlertSeverity = 'low' | 'medium' | 'high' | 'critical'; -interface ContextualFlyoutAlertsField { +interface ResultAlertsField { _id: string[]; _index: string[]; 'kibana.alert.rule.uuid': string[]; @@ -46,8 +46,17 @@ interface ContextualFlyoutAlertsField { 'kibana.alert.rule.name': string[]; } +interface ContextualFlyoutAlertsField { + id: string; + index: string; + ruleUuid: string; + ruleName: string; + reason: string; + severity: AlertSeverity; +} + interface AlertsDetailsFields { - fields: ContextualFlyoutAlertsField; + fields: ResultAlertsField; } export const AlertsDetailsTable = memo( @@ -62,7 +71,7 @@ export const AlertsDetailsTable = memo( const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(10); - const alertsPagination = (alerts: AlertsDetailsFields[]) => { + const alertsPagination = (alerts: ContextualFlyoutAlertsField[]) => { let pageOfItems; if (!pageIndex && !pageSize) { @@ -88,12 +97,19 @@ export const AlertsDetailsTable = memo( const alertDataResults = (data?.hits?.hits as AlertsDetailsFields[])?.map( (item: AlertsDetailsFields) => { - return { fields: item.fields }; + return { + id: item.fields?._id[0], + index: item.fields?._index[0], + reason: item.fields?.['kibana.alert.reason'][0], + ruleName: item.fields?.['kibana.alert.rule.name'][0], + ruleUuid: item.fields?.['kibana.alert.rule.uuid'][0], + severity: item.fields?.['kibana.alert.severity'][0], + }; } ); - const severitiesMap = - alertDataResults?.map((item) => item.fields['kibana.alert.severity'][0]) || []; + const severitiesMap = alertDataResults?.map((item) => item.severity) || []; + const alertStats = Object.entries( severitiesMap.reduce((acc: Record, item) => { acc[item] = (acc[item] || 0) + 1; @@ -114,7 +130,7 @@ export const AlertsDetailsTable = memo( pageSizeOptions: [10, 25, 100], }; - const onTableChange = ({ page }: Criteria) => { + const onTableChange = ({ page }: Criteria) => { if (page) { const { index, size } = page; setPageIndex(index); @@ -142,26 +158,20 @@ export const AlertsDetailsTable = memo( const tableId = TableId.alertsOnRuleDetailsPage; - const columns: Array> = [ + const columns: Array> = [ { - field: 'fields', + field: 'id', name: '', width: '5%', - render: (field: ContextualFlyoutAlertsField) => ( - - handleOnEventAlertDetailPanelOpened(field._id[0], field._index[0], tableId) - } - > + render: (id: string, alert: ContextualFlyoutAlertsField) => ( + handleOnEventAlertDetailPanelOpened(id, alert.index, tableId)}> ), }, { - field: 'fields', - render: (field: ContextualFlyoutAlertsField) => ( - {field['kibana.alert.rule.name'][0]} - ), + field: 'ruleName', + render: (ruleName: string) => {ruleName}, name: i18n.translate( 'xpack.securitySolution.flyout.left.insights.alerts.table.ruleNameColumnName', { @@ -171,13 +181,10 @@ export const AlertsDetailsTable = memo( width: '35%', }, { - field: 'fields', - render: (field: ContextualFlyoutAlertsField) => ( + field: 'severity', + render: (severity: AlertSeverity) => ( - + ), name: i18n.translate( @@ -189,10 +196,8 @@ export const AlertsDetailsTable = memo( width: '20%', }, { - field: 'fields', - render: (field: ContextualFlyoutAlertsField) => ( - {field['kibana.alert.reason'][0]} - ), + field: 'reason', + render: (reason: string) => {reason}, name: i18n.translate( 'xpack.securitySolution.flyout.left.insights.alerts.table.reasonColumnName', { From 8cff4f79362b0f4f601ab784a4db68c1bd15d03b Mon Sep 17 00:00:00 2001 From: animehart Date: Thu, 14 Nov 2024 00:00:21 -0800 Subject: [PATCH 21/22] reversed distribution bar array --- .../csp_details/alerts_findings_details_table.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx index fa9ebc2fc285..b376b2c7e4f3 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -98,12 +98,12 @@ export const AlertsDetailsTable = memo( const alertDataResults = (data?.hits?.hits as AlertsDetailsFields[])?.map( (item: AlertsDetailsFields) => { return { - id: item.fields?._id[0], - index: item.fields?._index[0], - reason: item.fields?.['kibana.alert.reason'][0], - ruleName: item.fields?.['kibana.alert.rule.name'][0], - ruleUuid: item.fields?.['kibana.alert.rule.uuid'][0], - severity: item.fields?.['kibana.alert.severity'][0], + id: item.fields?._id?.[0], + index: item.fields?._index?.[0], + reason: item.fields?.['kibana.alert.reason']?.[0], + ruleName: item.fields?.['kibana.alert.rule.name']?.[0], + ruleUuid: item.fields?.['kibana.alert.rule.uuid']?.[0], + severity: item.fields?.['kibana.alert.severity']?.[0], }; } ); @@ -246,7 +246,7 @@ export const AlertsDetailsTable = memo( - + Date: Thu, 14 Nov 2024 09:06:04 -0800 Subject: [PATCH 22/22] pr comments + replaced reason column with status --- .../common/utils/helpers.test.ts | 2 +- .../common/utils/helpers.ts | 2 +- .../alerts_findings_details_table.tsx | 18 +++++++++--------- .../csp_details/insights_tab_csp.tsx | 2 -- .../flyout/entity_details/host_right/index.tsx | 9 ++------- .../flyout/entity_details/user_right/index.tsx | 9 ++------- 6 files changed, 15 insertions(+), 27 deletions(-) diff --git a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts index 01a5b2b24400..04cb76f4441c 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts @@ -193,9 +193,9 @@ describe('test helper methods', () => { '_id', '_index', 'kibana.alert.rule.uuid', - 'kibana.alert.reason', 'kibana.alert.severity', 'kibana.alert.rule.name', + 'kibana.alert.workflow_status', ], query: { bool: { diff --git a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts index d9b43642000e..bd531fa63804 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts @@ -78,9 +78,9 @@ export const buildEntityAlertsQuery = ( '_id', '_index', 'kibana.alert.rule.uuid', - 'kibana.alert.reason', 'kibana.alert.severity', 'kibana.alert.rule.name', + 'kibana.alert.workflow_status', ], query: { bool: { diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx index b376b2c7e4f3..966de68e3497 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -41,9 +41,9 @@ interface ResultAlertsField { _id: string[]; _index: string[]; 'kibana.alert.rule.uuid': string[]; - 'kibana.alert.reason': string[]; 'kibana.alert.severity': AlertSeverity[]; 'kibana.alert.rule.name': string[]; + 'kibana.alert.workflow_status': string[]; } interface ContextualFlyoutAlertsField { @@ -51,8 +51,8 @@ interface ContextualFlyoutAlertsField { index: string; ruleUuid: string; ruleName: string; - reason: string; severity: AlertSeverity; + status: string; } interface AlertsDetailsFields { @@ -100,10 +100,10 @@ export const AlertsDetailsTable = memo( return { id: item.fields?._id?.[0], index: item.fields?._index?.[0], - reason: item.fields?.['kibana.alert.reason']?.[0], ruleName: item.fields?.['kibana.alert.rule.name']?.[0], ruleUuid: item.fields?.['kibana.alert.rule.uuid']?.[0], severity: item.fields?.['kibana.alert.severity']?.[0], + status: item.fields?.['kibana.alert.workflow_status']?.[0], }; } ); @@ -178,7 +178,7 @@ export const AlertsDetailsTable = memo( defaultMessage: 'Rule', } ), - width: '35%', + width: '55%', }, { field: 'severity', @@ -196,15 +196,15 @@ export const AlertsDetailsTable = memo( width: '20%', }, { - field: 'reason', - render: (reason: string) => {reason}, + field: 'status', + render: (status: string) => {capitalize(status)}, name: i18n.translate( - 'xpack.securitySolution.flyout.left.insights.alerts.table.reasonColumnName', + 'xpack.securitySolution.flyout.left.insights.alerts.table.statusColumnName', { - defaultMessage: 'Reason', + defaultMessage: 'Status', } ), - width: '40%', + width: '20%', }, ]; diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/insights_tab_csp.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/insights_tab_csp.tsx index 17e75a7ca88f..2e7b4171fd02 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/insights_tab_csp.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/insights_tab_csp.tsx @@ -12,7 +12,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { FlyoutPanelProps, PanelPath } from '@kbn/expandable-flyout'; import { useExpandableFlyoutState } from '@kbn/expandable-flyout'; import { i18n } from '@kbn/i18n'; -// import type { FlyoutPanels } from '@kbn/expandable-flyout/src/store/state'; import { CspInsightLeftPanelSubTab } from '../../../flyout/entity_details/shared/components/left_panel/left_panel_header'; import { MisconfigurationFindingsDetailsTable } from './misconfiguration_findings_details_table'; import { VulnerabilitiesFindingsDetailsTable } from './vulnerabilities_findings_details_table'; @@ -155,7 +154,6 @@ export const InsightsTabCsp = memo( ) : activeInsightsId === CspInsightLeftPanelSubTab.VULNERABILITIES ? ( ) : ( - //
{'ALERTS HERE'}
//AlertsDetailsTable )} diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx index e178cda2c90d..a7e99898606f 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx @@ -135,13 +135,8 @@ export const HostPanel = ({ from, }); - const alertsOpenCount = alertsData?.open?.total || 0; - - const alertsAcknowledgedCount = alertsData?.acknowledged?.total || 0; - - const alertsCount = alertsOpenCount + alertsAcknowledgedCount; - - const hasNonClosedAlerts = alertsCount; + const hasNonClosedAlerts = + (alertsData?.acknowledged?.total || 0) + (alertsData?.open?.total || 0) > 0; useQueryInspector({ deleteQuery, diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx index 7fade9677b77..07762ed9aea0 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx @@ -127,13 +127,8 @@ export const UserPanel = ({ from, }); - const alertsOpenCount = alertsData?.open?.total || 0; - - const alertsAcknowledgedCount = alertsData?.acknowledged?.total || 0; - - const alertsCount = alertsOpenCount + alertsAcknowledgedCount; - - const hasNonClosedAlerts = alertsCount > 0; + const hasNonClosedAlerts = + (alertsData?.acknowledged?.total || 0) + (alertsData?.open?.total || 0) > 0; useQueryInspector({ deleteQuery,