Skip to content

Commit

Permalink
[Security Solution][Alert Flyout] Update entity insight badge to open…
Browse files Browse the repository at this point in the history
… entity flyouts (elastic#208287)

## Summary

This PR updates the cloud insights in entity section to open to entity
flyout. When examining the insights (for example, the host is shown to
have 50 alerts, instead of opening the preview, and user click on the
details and go to respective tabs, this PR updated the behavior to open
the details tab via 1 click. The goal is to reduce friction for users
during investigation.

Feature flag: `newExpandableFlyoutNavigationEnabled`

When flag is off:
- Click on alert count should open timeline (if user has timeline
privileage)
- Click on misconfigurations and vulnerabilities badge should open
host/user preview


https://github.com/user-attachments/assets/23e0cc40-129d-4e75-b5be-26a49dcad710


When flag is on:
- Click on count badges should open the respective entity flyout and the
insights details tab



https://github.com/user-attachments/assets/5dfc39b7-edae-4b76-9a3a-79326337cb3b


### Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
  • Loading branch information
christineweng authored Feb 10, 2025
1 parent 5f70855 commit 6e61f52
Show file tree
Hide file tree
Showing 11 changed files with 418 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ import {
MISCONFIGURATION_INSIGHT_HOST_DETAILS,
VULNERABILITIES_INSIGHT_HOST_DETAILS,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { useHasMisconfigurations } from '@kbn/cloud-security-posture/src/hooks/use_has_misconfigurations';
import { useHasVulnerabilities } from '@kbn/cloud-security-posture/src/hooks/use_has_vulnerabilities';
import { useNonClosedAlerts } from '../../../../cloud_security_posture/hooks/use_non_closed_alerts';
import { ExpandablePanel } from '../../../shared/components/expandable_panel';
import type { RelatedUser } from '../../../../../common/search_strategy/security_solution/related_entities/related_users';
import type { RiskSeverity } from '../../../../../common/search_strategy';
import { HostOverview } from '../../../../overview/components/host_overview';
import { AnomalyTableProvider } from '../../../../common/components/ml/anomaly/anomaly_table_provider';
import { InspectButton, InspectButtonContainer } from '../../../../common/components/inspect';
import { EntityType } from '../../../../../common/entity_analytics/types';
import { EntityIdentifierFields, EntityType } from '../../../../../common/entity_analytics/types';
import { RiskScoreLevel } from '../../../../entity_analytics/components/severity/common';
import { DefaultFieldRenderer } from '../../../../timelines/components/field_renderers/default_renderer';
import { InputsModelId } from '../../../../common/store/inputs/constants';
Expand Down Expand Up @@ -74,9 +77,13 @@ import { MisconfigurationsInsight } from '../../shared/components/misconfigurati
import { VulnerabilitiesInsight } from '../../shared/components/vulnerabilities_insight';
import { AlertCountInsight } from '../../shared/components/alert_count_insight';
import { DocumentEventTypes } from '../../../../common/lib/telemetry';
import { useNavigateToHostDetails } from '../../../entity_details/host_right/hooks/use_navigate_to_host_details';
import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
import { buildHostNamesFilter } from '../../../../../common/search_strategy';

const HOST_DETAILS_ID = 'entities-hosts-details';
const RELATED_USERS_ID = 'entities-hosts-related-users';
const HOST_DETAILS_INSIGHTS_ID = 'host-details-insights';

const HostOverviewManage = manageQuery(HostOverview);
const RelatedUsersManage = manageQuery(InspectButtonContainer);
Expand Down Expand Up @@ -113,6 +120,14 @@ export const HostDetails: React.FC<HostDetailsProps> = ({ hostName, timestamp, s

const { openPreviewPanel } = useExpandableFlyoutApi();

const timerange = useMemo(
() => ({
from,
to,
}),
[from, to]
);

const narrowDateRange = useCallback<NarrowDateRange>(
(score, interval) => {
const fromTo = scoreIntervalToDateTime(score, interval);
Expand Down Expand Up @@ -151,6 +166,40 @@ export const HostDetails: React.FC<HostDetailsProps> = ({ hostName, timestamp, s
skip: selectedPatterns.length === 0,
});

const filterQuery = useMemo(
() => (hostName ? buildHostNamesFilter([hostName]) : undefined),
[hostName]
);
const { data: hostRisk } = useRiskScore({
filterQuery,
riskEntity: EntityType.host,
skip: hostName == null,
timerange,
});
const hostRiskData = hostRisk && hostRisk.length > 0 ? hostRisk[0] : undefined;
const isRiskScoreExist = !!hostRiskData?.host.risk;

const { hasNonClosedAlerts } = useNonClosedAlerts({
field: EntityIdentifierFields.hostName,
value: hostName,
to,
from,
queryId: 'HostEntityOverview',
});
const { hasMisconfigurationFindings } = useHasMisconfigurations('host.name', hostName);
const { hasVulnerabilitiesFindings } = useHasVulnerabilities('host.name', hostName);

const { openDetailsPanel } = useNavigateToHostDetails({
hostName,
scopeId,
isRiskScoreExist,
hasMisconfigurationFindings,
hasVulnerabilitiesFindings,
hasNonClosedAlerts,
isPreviewMode: true, // setting to true to always open a new host flyout
contextID: HOST_DETAILS_INSIGHTS_ID,
});

const {
loading: isRelatedUsersLoading,
inspect: inspectRelatedUsers,
Expand Down Expand Up @@ -342,18 +391,21 @@ export const HostDetails: React.FC<HostDetailsProps> = ({ hostName, timestamp, s
fieldName={'host.name'}
name={hostName}
direction="column"
openDetailsPanel={openDetailsPanel}
data-test-subj={HOST_DETAILS_ALERT_COUNT_TEST_ID}
/>
<MisconfigurationsInsight
fieldName={'host.name'}
name={hostName}
direction="column"
openDetailsPanel={openDetailsPanel}
data-test-subj={HOST_DETAILS_MISCONFIGURATIONS_TEST_ID}
telemetryKey={MISCONFIGURATION_INSIGHT_HOST_DETAILS}
/>
<VulnerabilitiesInsight
hostName={hostName}
direction="column"
openDetailsPanel={openDetailsPanel}
data-test-subj={HOST_DETAILS_VULNERABILITIES_TEST_ID}
telemetryKey={VULNERABILITIES_INSIGHT_HOST_DETAILS}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { MISCONFIGURATION_INSIGHT_USER_DETAILS } from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { useHasMisconfigurations } from '@kbn/cloud-security-posture/src/hooks/use_has_misconfigurations';
import { useNonClosedAlerts } from '../../../../cloud_security_posture/hooks/use_non_closed_alerts';
import { ExpandablePanel } from '../../../shared/components/expandable_panel';
import type { RelatedHost } from '../../../../../common/search_strategy/security_solution/related_entities/related_hosts';
import type { RiskSeverity } from '../../../../../common/search_strategy';
import { UserOverview } from '../../../../overview/components/user_overview';
import { AnomalyTableProvider } from '../../../../common/components/ml/anomaly/anomaly_table_provider';
import { InspectButton, InspectButtonContainer } from '../../../../common/components/inspect';
import { EntityType } from '../../../../../common/entity_analytics/types';
import { EntityIdentifierFields, EntityType } from '../../../../../common/entity_analytics/types';
import { RiskScoreLevel } from '../../../../entity_analytics/components/severity/common';
import { DefaultFieldRenderer } from '../../../../timelines/components/field_renderers/default_renderer';
import { CellActions } from '../../shared/components/cell_actions';
Expand Down Expand Up @@ -69,9 +71,13 @@ import type { NarrowDateRange } from '../../../../common/components/ml/types';
import { MisconfigurationsInsight } from '../../shared/components/misconfiguration_insight';
import { AlertCountInsight } from '../../shared/components/alert_count_insight';
import { DocumentEventTypes } from '../../../../common/lib/telemetry';
import { useNavigateToUserDetails } from '../../../entity_details/user_right/hooks/use_navigate_to_user_details';
import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
import { buildUserNamesFilter } from '../../../../../common/search_strategy';

const USER_DETAILS_ID = 'entities-users-details';
const RELATED_HOSTS_ID = 'entities-users-related-hosts';
const USER_DETAILS_INSIGHTS_ID = 'user-details-insights';

const UserOverviewManage = manageQuery(UserOverview);
const RelatedHostsManage = manageQuery(InspectButtonContainer);
Expand Down Expand Up @@ -109,6 +115,14 @@ export const UserDetails: React.FC<UserDetailsProps> = ({ userName, timestamp, s

const { openPreviewPanel } = useExpandableFlyoutApi();

const timerange = useMemo(
() => ({
from,
to,
}),
[from, to]
);

const narrowDateRange = useCallback<NarrowDateRange>(
(score, interval) => {
const fromTo = scoreIntervalToDateTime(score, interval);
Expand Down Expand Up @@ -138,6 +152,41 @@ export const UserDetails: React.FC<UserDetailsProps> = ({ userName, timestamp, s
});
}, [openPreviewPanel, userName, scopeId, telemetry]);

const filterQuery = useMemo(
() => (userName ? buildUserNamesFilter([userName]) : undefined),
[userName]
);

const { data: userRisk } = useRiskScore({
filterQuery,
riskEntity: EntityType.user,
timerange,
});
const userRiskData = userRisk && userRisk.length > 0 ? userRisk[0] : undefined;
const isRiskScoreExist = !!userRiskData?.user.risk;

const { hasMisconfigurationFindings } = useHasMisconfigurations(
EntityIdentifierFields.userName,
userName
);
const { hasNonClosedAlerts } = useNonClosedAlerts({
field: EntityIdentifierFields.userName,
value: userName,
to,
from,
queryId: USER_DETAILS_INSIGHTS_ID,
});

const { openDetailsPanel } = useNavigateToUserDetails({
userName,
scopeId,
isRiskScoreExist,
hasMisconfigurationFindings,
hasNonClosedAlerts,
isPreviewMode: true, // setting to true to always open a new user flyout
contextID: USER_DETAILS_INSIGHTS_ID,
});

const [isUserLoading, { inspect, userDetails, refetch }] = useObservedUserDetails({
id: userDetailsQueryId,
startDate: from,
Expand Down Expand Up @@ -339,12 +388,14 @@ export const UserDetails: React.FC<UserDetailsProps> = ({ userName, timestamp, s
fieldName={'user.name'}
name={userName}
direction="column"
openDetailsPanel={openDetailsPanel}
data-test-subj={USER_DETAILS_ALERT_COUNT_TEST_ID}
/>
<MisconfigurationsInsight
fieldName={'user.name'}
name={userName}
direction="column"
openDetailsPanel={openDetailsPanel}
data-test-subj={USER_DETAILS_MISCONFIGURATIONS_TEST_ID}
telemetryKey={MISCONFIGURATION_INSIGHT_USER_DETAILS}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import {
MISCONFIGURATION_INSIGHT_HOST_ENTITY_OVERVIEW,
VULNERABILITIES_INSIGHT_HOST_ENTITY_OVERVIEW,
} from '@kbn/cloud-security-posture-common/utils/ui_metrics';
import { useHasMisconfigurations } from '@kbn/cloud-security-posture/src/hooks/use_has_misconfigurations';
import { useHasVulnerabilities } from '@kbn/cloud-security-posture/src/hooks/use_has_vulnerabilities';
import { useNonClosedAlerts } from '../../../../cloud_security_posture/hooks/use_non_closed_alerts';
import { buildHostNamesFilter } from '../../../../../common/search_strategy';
import { HOST_NAME_FIELD_NAME } from '../../../../timelines/components/timeline/body/renderers/constants';
import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
Expand All @@ -31,7 +34,7 @@ import {
FirstLastSeen,
FirstLastSeenType,
} from '../../../../common/components/first_last_seen/first_last_seen';
import { EntityType } from '../../../../../common/entity_analytics/types';
import { EntityIdentifierFields, EntityType } from '../../../../../common/entity_analytics/types';
import { getEmptyTagValue } from '../../../../common/components/empty_value';
import { DescriptionListStyled } from '../../../../common/components/page';
import { OverviewDescriptionList } from '../../../../common/components/overview_description_list';
Expand Down Expand Up @@ -62,8 +65,10 @@ import { PreviewLink } from '../../../shared/components/preview_link';
import { MisconfigurationsInsight } from '../../shared/components/misconfiguration_insight';
import { VulnerabilitiesInsight } from '../../shared/components/vulnerabilities_insight';
import { AlertCountInsight } from '../../shared/components/alert_count_insight';
import { useNavigateToHostDetails } from '../../../entity_details/host_right/hooks/use_navigate_to_host_details';

const HOST_ICON = 'storage';
const HOST_ENTITY_OVERVIEW_ID = 'host-entity-overview';

export interface HostEntityOverviewProps {
/**
Expand Down Expand Up @@ -111,6 +116,8 @@ export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName
skip: hostName == null,
timerange,
});
const hostRiskData = hostRisk && hostRisk.length > 0 ? hostRisk[0] : undefined;
const isRiskScoreExist = !!hostRiskData?.host.risk;

const [isHostDetailsLoading, { hostDetails }] = useHostDetails({
hostName,
Expand Down Expand Up @@ -159,9 +166,8 @@ export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName
const { euiTheme } = useEuiTheme();
const xsFontSize = useEuiFontSize('xs').fontSize;

const [hostRiskLevel] = useMemo(() => {
const hostRiskData = hostRisk && hostRisk.length > 0 ? hostRisk[0] : undefined;
return [
const [hostRiskLevel] = useMemo(
() => [
{
title: (
<EuiFlexGroup alignItems="flexEnd" gutterSize="none" responsive={false}>
Expand All @@ -181,8 +187,30 @@ export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName
</>
),
},
];
}, [hostRisk]);
],
[hostRiskData]
);

const { hasNonClosedAlerts } = useNonClosedAlerts({
field: EntityIdentifierFields.hostName,
value: hostName,
to,
from,
queryId: HOST_ENTITY_OVERVIEW_ID,
});
const { hasMisconfigurationFindings } = useHasMisconfigurations('host.name', hostName);
const { hasVulnerabilitiesFindings } = useHasVulnerabilities('host.name', hostName);

const { openDetailsPanel } = useNavigateToHostDetails({
hostName,
scopeId,
isRiskScoreExist,
hasMisconfigurationFindings,
hasVulnerabilitiesFindings,
hasNonClosedAlerts,
isPreviewMode: true, // setting to true to always open a new host flyout
contextID: 'HostEntityOverview',
});

return (
<EuiFlexGroup
Expand Down Expand Up @@ -251,16 +279,19 @@ export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName
<AlertCountInsight
fieldName={'host.name'}
name={hostName}
openDetailsPanel={openDetailsPanel}
data-test-subj={ENTITIES_HOST_OVERVIEW_ALERT_COUNT_TEST_ID}
/>
<MisconfigurationsInsight
fieldName={'host.name'}
name={hostName}
openDetailsPanel={openDetailsPanel}
data-test-subj={ENTITIES_HOST_OVERVIEW_MISCONFIGURATIONS_TEST_ID}
telemetryKey={MISCONFIGURATION_INSIGHT_HOST_ENTITY_OVERVIEW}
/>
<VulnerabilitiesInsight
hostName={hostName}
openDetailsPanel={openDetailsPanel}
data-test-subj={ENTITIES_HOST_OVERVIEW_VULNERABILITIES_TEST_ID}
telemetryKey={VULNERABILITIES_INSIGHT_HOST_ENTITY_OVERVIEW}
/>
Expand Down
Loading

0 comments on commit 6e61f52

Please sign in to comment.