Skip to content

Commit

Permalink
[SecuritySolution] Hover actions not working on the overview section …
Browse files Browse the repository at this point in the history
…of the host details page (elastic#210819)

Fixes elastic#210815

Steps to verify:
1. Ingest some data
2. Visit host details page
3. Hover onto host ID and IP address, verify filter in / filter out /
add to timeline / show top N works correctly.

https://github.com/user-attachments/assets/75148ebb-154d-42a4-ae75-127925564d8a
(cherry picked from commit b2db698)
  • Loading branch information
angorayc committed Feb 14, 2025
1 parent d7f4edd commit db11a20
Show file tree
Hide file tree
Showing 9 changed files with 53 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import { AlertCountByRuleByStatus } from '../../../../common/components/alert_co
import { useLicense } from '../../../../common/hooks/use_license';
import { ResponderActionButton } from '../../../../common/components/endpoint/responder';
import { useRefetchOverviewPageRiskScore } from '../../../../entity_analytics/api/hooks/use_refetch_overview_page_risk_score';
import { SourcererScopeName } from '../../../../sourcerer/store/model';

const ES_HOST_FIELD = 'host.name';
const HostOverviewManage = manageQuery(HostOverview);
Expand Down Expand Up @@ -265,6 +266,7 @@ const HostDetailsComponent: React.FC<HostDetailsProps> = ({ detailName, hostDeta
hostName={detailName}
indexNames={selectedPatterns}
jobNameById={jobNameById}
scopeId={SourcererScopeName.default}
/>
)}
</AnomalyTableProvider>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { mockData } from './mock';
import { mockAnomalies } from '../../../../common/components/ml/mock';
import type { NarrowDateRange } from '../../../../common/components/ml/types';
import { FlowTargetSourceDest } from '../../../../../common/search_strategy';
import { SourcererScopeName } from '../../../../sourcerer/store/model';

describe('IP Overview Component', () => {
describe('rendering', () => {
Expand All @@ -38,6 +39,7 @@ describe('IP Overview Component', () => {
}>,
indexPatterns: [],
jobNameById: {},
scopeId: SourcererScopeName.default,
};

test('it renders the default IP Overview', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml
import { hasMlUserPermissions } from '../../../../../common/machine_learning/has_ml_user_permissions';
import { InspectButton, InspectButtonContainer } from '../../../../common/components/inspect';
import { OverviewDescriptionList } from '../../../../common/components/overview_description_list';
import type { SourcererScopeName } from '../../../../sourcerer/store/model';

export interface IpOverviewProps {
anomaliesData: Anomalies | null;
Expand All @@ -52,6 +53,7 @@ export interface IpOverviewProps {
isLoadingAnomaliesData: boolean;
loading: boolean;
narrowDateRange: NarrowDateRange;
scopeId: SourcererScopeName;
startDate: string;
type: networkModel.NetworkType;
indexPatterns: string[];
Expand All @@ -73,6 +75,7 @@ export const IpOverview = React.memo<IpOverviewProps>(
isLoadingAnomaliesData,
anomaliesData,
narrowDateRange,
scopeId,
indexPatterns,
jobNameById,
}) => {
Expand Down Expand Up @@ -148,14 +151,14 @@ export const IpOverview = React.memo<IpOverviewProps>(
title: i18n.HOST_ID,
description:
typeData && data.host
? hostIdRenderer({ host: data.host, isDraggable, ipFilter: ip, contextID })
? hostIdRenderer({ host: data.host, isDraggable, ipFilter: ip, contextID, scopeId,})
: getEmptyTagValue(),
},
{
title: i18n.HOST_NAME,
description:
typeData && data.host
? hostNameRenderer(data.host, ip, contextID, isDraggable)
? hostNameRenderer(scopeId, data.host, ip, contextID, isDraggable)
: getEmptyTagValue(),
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import {
CellActionsMode,
SecurityCellActionsTrigger,
} from '../../../../common/components/cell_actions';
import { SourcererScopeName } from '../../../../sourcerer/store/model';

const NetworkDetailsManage = manageQuery(IpOverview);

Expand Down Expand Up @@ -220,6 +221,7 @@ const NetworkDetailsComponent: React.FC = () => {
narrowDateRange={narrowDateRange}
indexPatterns={selectedPatterns}
jobNameById={jobNameById}
scopeId={SourcererScopeName.default}
/>

<EuiHorizontalRule />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { useAnomaliesTableData } from '../../../common/components/ml/anomaly/use
import { useInstalledSecurityJobNameById } from '../../../common/components/ml/hooks/use_installed_security_jobs';
import { EmptyPrompt } from '../../../common/components/empty_prompt';
import type { NarrowDateRange } from '../../../common/components/ml/types';
import { SourcererScopeName } from '../../../sourcerer/store/model';

export interface NetworkDetailsProps {
/**
Expand Down Expand Up @@ -121,6 +122,7 @@ export const NetworkDetails = ({
narrowDateRange={narrowDateRange}
indexPatterns={selectedPatterns}
jobNameById={jobNameById}
scopeId={SourcererScopeName.default}
/>
) : (
<EmptyPrompt />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export const HostOverview = React.memo<HostSummaryProps>(
title: i18n.HOST_ID,
description:
data && data.host
? hostIdRenderer({ host: data.host, isDraggable, noLink: true })
? hostIdRenderer({ host: data.host, isDraggable, noLink: true, scopeId })
: getEmptyTagValue(),
},
{
Expand All @@ -205,7 +205,7 @@ export const HostOverview = React.memo<HostSummaryProps>(
),
},
],
[data, indexNames, hostName, isDraggable]
[data, scopeId, indexNames, hostName, isDraggable]
);
const firstColumn = useMemo(
() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type { AutonomousSystem } from '../../../../common/search_strategy';
import { FlowTarget } from '../../../../common/search_strategy';
import type { HostEcs } from '@kbn/securitysolution-ecs';
import { mockGetUrlForApp } from '@kbn/security-solution-navigation/mocks/context';
import { SourcererScopeName } from '../../../sourcerer/store/model';

jest.mock('../../../common/lib/kibana');
jest.mock('@kbn/security-solution-navigation/src/context');
Expand All @@ -32,6 +33,8 @@ mockGetUrlForApp.mockImplementation(
jest.mock('../../../common/hooks/use_get_field_spec');

describe('Field Renderers', () => {
const scopeId = SourcererScopeName.default;

describe('#locationRenderer', () => {
test('it renders correctly against snapshot', () => {
const { asFragment } = render(
Expand Down Expand Up @@ -104,54 +107,72 @@ describe('Field Renderers', () => {
describe('#hostIdRenderer', () => {
test('it renders correctly against snapshot', () => {
const { asFragment } = render(
<TestProviders>{hostNameRenderer(mockData.complete.host, '10.10.10.10')}</TestProviders>
<TestProviders>
{hostNameRenderer(scopeId, mockData.complete.host, '10.10.10.10')}
</TestProviders>
);
expect(asFragment()).toMatchSnapshot();
});

test('it renders emptyTagValue when non-matching IP is provided', () => {
render(
<TestProviders>{hostNameRenderer(mockData.complete.host, '10.10.10.11')}</TestProviders>
<TestProviders>
{hostNameRenderer(scopeId, mockData.complete.host, '10.10.10.11')}
</TestProviders>
);
expect(screen.getByText(getEmptyValue())).toBeInTheDocument();
});

test('it renders emptyTagValue when no host.id is provided', () => {
render(<TestProviders>{hostNameRenderer(emptyIdHost, FlowTarget.source)}</TestProviders>);
render(
<TestProviders>{hostNameRenderer(scopeId, emptyIdHost, FlowTarget.source)}</TestProviders>
);
expect(screen.getByText(getEmptyValue())).toBeInTheDocument();
});
test('it renders emptyTagValue when no host.ip is provided', () => {
render(<TestProviders>{hostNameRenderer(emptyIpHost, FlowTarget.source)}</TestProviders>);
render(
<TestProviders>{hostNameRenderer(scopeId, emptyIpHost, FlowTarget.source)}</TestProviders>
);
expect(screen.getByText(getEmptyValue())).toBeInTheDocument();
});
});

describe('#hostNameRenderer', () => {
test('it renders correctly against snapshot', () => {
const { asFragment } = render(
<TestProviders>{hostNameRenderer(mockData.complete.host, '10.10.10.10')}</TestProviders>
<TestProviders>
{hostNameRenderer(scopeId, mockData.complete.host, '10.10.10.10')}
</TestProviders>
);

expect(asFragment()).toMatchSnapshot();
});

test('it renders emptyTagValue when non-matching IP is provided', () => {
render(
<TestProviders>{hostNameRenderer(mockData.complete.host, '10.10.10.11')}</TestProviders>
<TestProviders>
{hostNameRenderer(scopeId, mockData.complete.host, '10.10.10.11')}
</TestProviders>
);
expect(screen.getByText(getEmptyValue())).toBeInTheDocument();
});

test('it renders emptyTagValue when no host.id is provided', () => {
render(<TestProviders>{hostNameRenderer(emptyIdHost, FlowTarget.source)}</TestProviders>);
render(
<TestProviders>{hostNameRenderer(scopeId, emptyIdHost, FlowTarget.source)}</TestProviders>
);
expect(screen.getByText(getEmptyValue())).toBeInTheDocument();
});
test('it renders emptyTagValue when no host.ip is provided', () => {
render(<TestProviders>{hostNameRenderer(emptyIpHost, FlowTarget.source)}</TestProviders>);
render(
<TestProviders>{hostNameRenderer(scopeId, emptyIpHost, FlowTarget.source)}</TestProviders>
);
expect(screen.getByText(getEmptyValue())).toBeInTheDocument();
});
test('it renders emptyTagValue when no host.name is provided', () => {
render(<TestProviders>{hostNameRenderer(emptyNameHost, FlowTarget.source)}</TestProviders>);
render(
<TestProviders>{hostNameRenderer(scopeId, emptyNameHost, FlowTarget.source)}</TestProviders>
);
expect(screen.getByText(getEmptyValue())).toBeInTheDocument();
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { DefaultDraggable } from '../../../common/components/draggables';
import { getEmptyTagValue } from '../../../common/components/empty_value';
import { HostDetailsLink, ReputationLink, WhoIsLink } from '../../../common/components/links';
import * as i18n from '../../../explore/network/components/details/translations';
import type { SourcererScopeName } from '../../../sourcerer/store/model';

export const IpOverviewId = 'ip-overview';

Expand Down Expand Up @@ -97,6 +98,7 @@ interface HostIdRendererTypes {
ipFilter?: string;
isDraggable?: boolean;
noLink?: boolean;
scopeId: string | undefined;
}

export const hostIdRenderer = ({
Expand All @@ -105,6 +107,7 @@ export const hostIdRenderer = ({
isDraggable = false,
ipFilter,
noLink,
scopeId,
}: HostIdRendererTypes): React.ReactElement =>
host.id && host.ip && (ipFilter == null || host.ip.includes(ipFilter)) ? (
<>
Expand All @@ -118,6 +121,7 @@ export const hostIdRenderer = ({
value={host.id[0]}
isAggregatable={true}
fieldType={'keyword'}
scopeId={scopeId}
>
{noLink ? (
<>{host.id}</>
Expand All @@ -134,6 +138,7 @@ export const hostIdRenderer = ({
);

export const hostNameRenderer = (
scopeId: SourcererScopeName,
host?: HostEcs,
ipFilter?: string,
contextID?: string,
Expand All @@ -153,6 +158,7 @@ export const hostNameRenderer = (
value={host.name[0]}
isAggregatable={true}
fieldType={'keyword'}
scopeId={scopeId}
>
<HostDetailsLink hostName={host.name[0]}>
{host.name ? host.name : getEmptyTagValue()}
Expand Down

0 comments on commit db11a20

Please sign in to comment.