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..e0199ab40168
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx
@@ -0,0 +1,76 @@
+/*
+ * 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 { 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 = {
+ open: {
+ total: 3,
+ severities: [
+ { key: 'low', value: 2, label: 'Low' },
+ { key: 'medium', value: 1, label: 'Medium' },
+ ],
+ },
+ acknowledged: {
+ total: 2,
+ severities: [
+ { key: 'low', value: 1, label: 'Low' },
+ { key: 'high', value: 1, label: 'High' },
+ ],
+ },
+};
+
+jest.mock(
+ '../../../detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data'
+);
+jest.mock('@kbn/expandable-flyout');
+
+describe('AlertsPreview', () => {
+ const mockOpenLeftPanel = jest.fn();
+
+ beforeEach(() => {
+ (useExpandableFlyoutApi as jest.Mock).mockReturnValue({ openLeftPanel: mockOpenLeftPanel });
+ });
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders', () => {
+ const { getByTestId } = render(
+
+
+
+ );
+
+ expect(getByTestId('securitySolutionFlyoutInsightsAlertsTitleText')).toBeInTheDocument();
+ });
+
+ it('renders correct alerts number', () => {
+ const { getByTestId } = render(
+
+
+
+ );
+
+ expect(getByTestId('securitySolutionFlyoutInsightsAlertsCount').textContent).toEqual('5');
+ });
+
+ it('should render the correct number of distribution bar section based on the number of severities', () => {
+ const { queryAllByTestId } = render(
+
+
+
+ );
+
+ expect(queryAllByTestId('AlertsPreviewDistributionBarTestId__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
new file mode 100644
index 000000000000..3f9a0115d9ed
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx
@@ -0,0 +1,121 @@
+/*
+ * 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 { capitalize } from 'lodash';
+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 type {
+ AlertsByStatus,
+ ParsedAlertsData,
+} from '../../../overview/components/detection_response/alerts_by_status/types';
+
+const AlertsCount = ({
+ alertsTotal,
+ euiTheme,
+}: {
+ alertsTotal: number;
+ euiTheme: EuiThemeComputed<{}>;
+}) => {
+ return (
+
+
+
+
+
+ {getAbbreviatedNumber(alertsTotal)}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export const AlertsPreview = ({
+ alertsData,
+ isPreviewMode,
+}: {
+ alertsData: ParsedAlertsData;
+ isPreviewMode?: boolean;
+}) => {
+ const { euiTheme } = useEuiTheme();
+
+ const severityMap = new Map();
+
+ (Object.keys(alertsData || {}) as AlertsByStatus[]).forEach((status) => {
+ if (alertsData?.[status]?.severities) {
+ alertsData?.[status]?.severities.forEach((severity) => {
+ const currentSeverity = severityMap.get(severity.key) || 0;
+ severityMap.set(severity.key, currentSeverity + severity.value);
+ });
+ }
+ });
+
+ const alertStats = Array.from(severityMap, ([key, count]) => ({
+ key: capitalize(key),
+ count,
+ color: getSeverityColor(key),
+ }));
+
+ const totalAlertsCount = alertStats.reduce((total, item) => total + item.count, 0);
+
+ return (
+
+
+
+ ),
+ }}
+ 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 eee9af194ca3..a43b56876f1a 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,15 +7,22 @@
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 { FILTER_CLOSED } from '../../../common/types';
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 type { ParsedAlertsData } from '../../overview/components/detection_response/alerts_by_status/types';
+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,
@@ -60,6 +67,39 @@ export const EntityInsight = ({
const isVulnerabilitiesFindingForHost = hasVulnerabilitiesFindings && fieldName === 'host.name';
+ 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 filteredAlertsData: ParsedAlertsData = alertsData
+ ? Object.fromEntries(Object.entries(alertsData).filter(([key]) => key !== FILTER_CLOSED))
+ : {};
+
+ const alertsOpenCount = filteredAlertsData?.open?.total || 0;
+
+ const alertsAcknowledgedCount = filteredAlertsData?.acknowledged?.total || 0;
+
+ const alertsCount = alertsOpenCount + alertsAcknowledgedCount;
+
+ if (alertsCount > 0) {
+ insightContent.push(
+ <>
+
+
+ >
+ );
+ }
+
if (hasMisconfigurationFindings)
insightContent.push(
<>
@@ -76,7 +116,8 @@ export const EntityInsight = ({
);
return (
<>
- {(hasMisconfigurationFindings ||
+ {(insightContent.length > 0 ||
+ hasMisconfigurationFindings ||
(isVulnerabilitiesFindingForHost && hasVulnerabilitiesFindings)) && (
<>
),
}