Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
ea59f0e
initial impl
kelvtanv Apr 27, 2026
5d184b1
Merge remote-tracking branch 'upstream/main' into migrate-main-conten…
kelvtanv Apr 30, 2026
ef191eb
rename folder + add to highlighted fields
kelvtanv Apr 30, 2026
a3474ff
conditional link in column
kelvtanv Apr 30, 2026
ce7372b
rework ChildLink
kelvtanv Apr 30, 2026
74ea68b
rework childlink
kelvtanv Apr 30, 2026
8b7273d
wire up discover cell renderers and fix footer
kelvtanv May 1, 2026
5a4fa92
Merge remote-tracking branch 'upstream/main' into migrate-main-conten…
kelvtanv May 4, 2026
efb2f3c
should open new flyout
kelvtanv May 4, 2026
82dceb1
Merge remote-tracking branch 'upstream/main' into migrate-main-conten…
kelvtanv May 5, 2026
8c55d1e
fix merge errors
kelvtanv May 5, 2026
fdad31f
js docs
kelvtanv May 5, 2026
ca2a070
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine May 5, 2026
c50f0b6
Merge remote-tracking branch 'upstream/main' into migrate-main-conten…
kelvtanv May 12, 2026
662c556
address comments
kelvtanv May 12, 2026
14a87c9
rename entity_details to entity and fix folder structure
kelvtanv May 12, 2026
34e003c
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine May 12, 2026
f0c2ace
Merge remote-tracking branch 'upstream/main' into migrate-main-conten…
kelvtanv May 14, 2026
d092351
test
kelvtanv May 14, 2026
abf6578
Merge remote-tracking branch 'upstream/main' into migrate-main-conten…
kelvtanv May 14, 2026
79b96f3
fix tests
kelvtanv May 14, 2026
dfa3d3b
initial impl
kelvtanv May 14, 2026
cb543fd
fix merge errors
kelvtanv May 14, 2026
055f8bf
hide icons using props
kelvtanv May 14, 2026
96367d4
hide header icons
kelvtanv May 14, 2026
3d36812
rework tools flyout header
kelvtanv May 15, 2026
c2de935
hide add to timeline action in discover for risk_inputs_tab
kelvtanv May 15, 2026
4f1aa33
replace csp_insights tool with three focused tools for v2 host flyout
kelvtanv May 15, 2026
94cf444
[dev] add mock data for misconfiguration and vulnerability tools
kelvtanv May 15, 2026
a12cc54
link renderer for observed data
kelvtanv May 15, 2026
94cfb98
fix css + bug related to time filter
kelvtanv May 19, 2026
87be8c7
refactor
kelvtanv May 20, 2026
812bab3
Merge branch 'main' into migrate-main-content-for-host-flyout
kelvtanv May 20, 2026
350b889
Merge branch 'migrate-main-content-for-host-flyout' into migrate-host…
kelvtanv May 20, 2026
9d7c4a8
wrapper for details
kelvtanv May 20, 2026
670ba52
Revert "[dev] add mock data for misconfiguration and vulnerability to…
kelvtanv May 20, 2026
7fed915
pass entity id
kelvtanv May 20, 2026
b7651bd
ensure entity id is passed in
kelvtanv May 20, 2026
d4ba747
second pass
kelvtanv May 22, 2026
dfbaf68
ai review comments
kelvtanv May 22, 2026
297df77
add unit tests for v2 host tool panels and csp details
kelvtanv May 22, 2026
f2963b4
more ai review
kelvtanv May 22, 2026
6d7d320
address comments
kelvtanv May 26, 2026
ec249f8
Merge pull request #3 from kelvtanv/migrate-host-risk-summary
kelvtanv Jun 1, 2026
7c558cb
Merge remote-tracking branch 'upstream/main' into migrate-main-conten…
kelvtanv Jun 1, 2026
60d32a3
Changes from node scripts/check
kibanamachine Jun 1, 2026
f557183
ci
kelvtanv Jun 1, 2026
bacf93e
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine Jun 1, 2026
7a3000e
fix ci
kelvtanv Jun 1, 2026
144ffc0
fix tests: use async findByTestId for lazy-loaded components in build…
kelvtanv Jun 2, 2026
f08fd56
re-organize non-threat hunting related changes
kelvtanv Jun 2, 2026
f0f016d
Merge remote-tracking branch 'upstream/main' into migrate-main-conten…
kelvtanv Jun 2, 2026
b474c97
fix imports
kelvtanv Jun 2, 2026
92f19f8
lint
kelvtanv Jun 2, 2026
54dcc0c
revert unneeded change
kelvtanv Jun 2, 2026
af65bce
Merge branch 'main' into migrate-main-content-for-host-flyout
kelvtanv Jun 2, 2026
4e7b007
fix type errors in csp flyout v2 test files
kelvtanv Jun 2, 2026
03d6b10
Changes from node scripts/eslint_all_files --no-cache --fix
kibanamachine Jun 2, 2026
6111e1d
Merge branch 'main' into migrate-main-content-for-host-flyout
kelvtanv Jun 2, 2026
ce2c60b
Merge branch 'main' into migrate-main-content-for-host-flyout
kelvtanv Jun 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ export const SIGNAL_RULE_NAME_FIELD_NAME = 'kibana.alert.rule.name';
export const LEGACY_SIGNAL_RULE_NAME_FIELD_NAME = 'signal.rule.name';
export const ALERT_WORKFLOW_STATUS_FIELD_NAME = 'kibana.alert.workflow_status';

export const HOST_NAME_FIELD = 'host.name';
export const HOST_HOSTNAME_FIELD = 'host.hostname';

// Also see: x-pack/solutions/security/plugins/security_solution/public/one_discover/cell_renderers/cell_renderers.tsx
export const ALLOWED_CELL_RENDER_FIELDS = [
ALERT_WORKFLOW_STATUS_FIELD_NAME,
SIGNAL_RULE_NAME_FIELD_NAME,
LEGACY_SIGNAL_RULE_NAME_FIELD_NAME,
HOST_NAME_FIELD,
HOST_HOSTNAME_FIELD,
];
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ import {
type RiskStats,
} from '../../../common/search_strategy';
import { useUiSetting, useKibana } from '../../common/lib/kibana';
import { HostPanelContent } from '../../flyout/entity_details/host_right/content';
import { HostPanelHeader } from '../../flyout/entity_details/host_right/header';
import { useObservedHost } from '../../flyout/entity_details/host_right/hooks/use_observed_host';
import { Content as HostPanelContent } from '../../flyout_v2/entity/host/main/content';
import { Header as HostPanelHeader } from '../../flyout_v2/entity/host/main/header';
import { useObservedHost } from '../../flyout_v2/entity/host/main/hooks/use_observed_host';
import { EntityType } from '../../../common/entity_analytics/types';
import {
buildRiskScoreStateFromEntityRecord,
Expand All @@ -49,8 +49,9 @@ import {
mergeLegacyIdentityWhenStoreEntityMissing,
type IdentityFields,
} from '../../flyout/document_details/shared/utils';
import { HOST_PANEL_RISK_SCORE_QUERY_ID } from '../../flyout/entity_details/host_right/constants';
import { HOST_PANEL_RISK_SCORE_QUERY_ID } from '../../flyout_v2/entity/host/main/constants';
import { FlyoutBody } from '../../flyout/shared/components/flyout_body';
import { FlyoutHeader } from '../../flyout/shared/components/flyout_header';
import {
useEntityPanelTabs,
TABLE_TAB_ID,
Expand Down Expand Up @@ -464,19 +465,21 @@ const HostEntityFlyoutOverviewCanvas: React.FC<{

return (
<>
<HostPanelHeader
hostName={hostName}
lastSeen={observedHost.lastSeen}
entityId={panelDisplayEntityId}
identityFields={documentEntityIdentifiers}
isEntityInStore={!!observedHost.entityRecord}
riskLevel={
observedHost.entityRecord
? ((getRiskFromEntityRecord(observedHost.entityRecord)?.calculated_level ??
'Unknown') as RiskSeverity)
: undefined
}
/>
<FlyoutHeader>
<HostPanelHeader
hostName={hostName}
lastSeen={observedHost.lastSeen}
entityId={panelDisplayEntityId}
identityFields={documentEntityIdentifiers}
isEntityInStore={!!observedHost.entityRecord}
riskLevel={
observedHost.entityRecord
? ((getRiskFromEntityRecord(observedHost.entityRecord)?.calculated_level ??
'Unknown') as RiskSeverity)
: undefined
}
/>
</FlyoutHeader>
<FlyoutBody>
{observedHost.entityRecord && (
<EntitySummaryGrid
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* 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 type { ParsedAlertsData } from '../../../../overview/components/detection_response/alerts_by_status/types';
import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview';
import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview';

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('@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview');
jest.mock('@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview');

describe('AlertsPreview (v2)', () => {
const mockOpenDetailsPanel = jest.fn();

beforeEach(() => {
(useVulnerabilitiesPreview as jest.Mock).mockReturnValue({
data: { count: { CRITICAL: 0, HIGH: 1, MEDIUM: 1, LOW: 0, UNKNOWN: 0 } },
});
(useMisconfigurationPreview as jest.Mock).mockReturnValue({
data: { count: { passed: 1, failed: 1 } },
});
});
afterEach(() => {
jest.clearAllMocks();
});

it('renders', () => {
const { getByTestId } = render(
<TestProviders>
<AlertsPreview alertsData={mockAlertsData} openDetailsPanel={mockOpenDetailsPanel} />
</TestProviders>
);

expect(getByTestId('securitySolutionFlyoutInsightsAlertsTitleLink')).toBeInTheDocument();
});

it('renders correct alerts number', () => {
const { getByTestId } = render(
<TestProviders>
<AlertsPreview alertsData={mockAlertsData} openDetailsPanel={mockOpenDetailsPanel} />
</TestProviders>
);

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(
<TestProviders>
<AlertsPreview alertsData={mockAlertsData} openDetailsPanel={mockOpenDetailsPanel} />
</TestProviders>
);

expect(queryAllByTestId('AlertsPreviewDistributionBarTestId__part').length).toEqual(3);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { useCallback, useMemo } from 'react';
import { css } from '@emotion/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 type {
AlertsByStatus,
ParsedAlertsData,
} from '../../../../overview/components/detection_response/alerts_by_status/types';
import { ExpandablePanel } from '../../../../flyout_v2/shared/components/expandable_panel';
import { getSeverityColor } from '../../../../detections/components/alerts_kpis/severity_level_panel/helpers';
import type { EntityDetailsPath } from '../../../../flyout/entity_details/shared/components/left_panel/left_panel_header';
import {
CspInsightLeftPanelSubTab,
EntityDetailsLeftPanelTab,
} from '../../../../flyout/entity_details/shared/components/left_panel/left_panel_header';

const AlertsCount = ({
alertsTotal,
euiTheme,
}: {
alertsTotal: number;
euiTheme: EuiThemeComputed<{}>;
}) => {
return (
<EuiFlexItem>
<EuiFlexGroup direction="column" gutterSize="none">
<EuiFlexItem>
<EuiTitle size="s">
<h3 data-test-subj={'securitySolutionFlyoutInsightsAlertsCount'}>
{getAbbreviatedNumber(alertsTotal)}
</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem>
<EuiText
size="xs"
css={css`
font-weight: ${euiTheme.font.weight.semiBold};
`}
>
<FormattedMessage
id="xpack.securitySolution.flyout.right.insights.alerts.alertsCountDescription"
defaultMessage="Alerts"
/>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
);
};

export const AlertsPreview = ({
alertsData,
openDetailsPanel,
}: {
alertsData: ParsedAlertsData;
openDetailsPanel: (path: EntityDetailsPath) => void;
}) => {
const { euiTheme } = useEuiTheme();

const severityMap = new Map<string, number>();
const severityRank: Record<string, number> = {
critical: 4,
high: 3,
medium: 2,
low: 1,
};

(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]: [string, number]) => ({
key: capitalize(key),
count,
color: getSeverityColor(key, euiTheme),
sort: severityRank[key.toLowerCase()] || 0,
})).sort((a, b) => b.sort - a.sort);

const totalAlertsCount = alertStats.reduce((total, item) => total + item.count, 0);

const goToEntityInsightTab = useCallback(
() =>
openDetailsPanel({
tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS,
subTab: CspInsightLeftPanelSubTab.ALERTS,
}),
[openDetailsPanel]
);

const link = useMemo(
() => ({
callback: goToEntityInsightTab,
tooltip: (
<FormattedMessage
id="xpack.securitySolution.flyout.right.insights.alerts.alertsTooltip"
defaultMessage="Show all alerts"
/>
),
}),
[goToEntityInsightTab]
);
return (
<ExpandablePanel
header={{
iconType: '',
title: (
<EuiText
size="xs"
css={{
fontWeight: euiTheme.font.weight.bold,
}}
>
<FormattedMessage
id="xpack.securitySolution.flyout.right.insights.alerts.alertsTitle"
defaultMessage="Alerts"
/>
</EuiText>
),
link: totalAlertsCount > 0 ? link : undefined,
}}
data-test-subj={'securitySolutionFlyoutInsightsAlerts'}
>
<EuiFlexGroup gutterSize="none">
<AlertsCount alertsTotal={totalAlertsCount} euiTheme={euiTheme} />
<EuiFlexItem grow={2}>
<EuiFlexGroup direction="column" gutterSize="none">
<EuiFlexItem />
<EuiFlexItem>
<EuiSpacer />
<DistributionBar
stats={alertStats}
data-test-subj="AlertsPreviewDistributionBarTestId"
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</ExpandablePanel>
);
};
Loading
Loading