mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[SecuritySolution] Hover actions not working on the overview section of the host details page (#210819)
## Summary Fixes https://github.com/elastic/kibana/issues/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
This commit is contained in:
parent
789986ce48
commit
b2db698f03
9 changed files with 60 additions and 13 deletions
|
@ -81,6 +81,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);
|
||||
|
@ -265,6 +266,7 @@ const HostDetailsComponent: React.FC<HostDetailsProps> = ({ detailName, hostDeta
|
|||
hostName={detailName}
|
||||
indexNames={selectedPatterns}
|
||||
jobNameById={jobNameById}
|
||||
scopeId={SourcererScopeName.default}
|
||||
/>
|
||||
)}
|
||||
</AnomalyTableProvider>
|
||||
|
|
|
@ -147,6 +147,7 @@ exports[`IP Overview Component rendering it renders the default IP Overview 1`]
|
|||
jobNameById={Object {}}
|
||||
loading={false}
|
||||
narrowDateRange={[MockFunction]}
|
||||
scopeId="default"
|
||||
startDate="2019-06-15T06:00:00.000Z"
|
||||
type="details"
|
||||
updateFlowTargetAction={[MockFunction]}
|
||||
|
@ -300,6 +301,7 @@ exports[`IP Overview Component rendering it renders the side panel IP overview 1
|
|||
jobNameById={Object {}}
|
||||
loading={false}
|
||||
narrowDateRange={[MockFunction]}
|
||||
scopeId="default"
|
||||
startDate="2019-06-15T06:00:00.000Z"
|
||||
type="details"
|
||||
updateFlowTargetAction={[MockFunction]}
|
||||
|
|
|
@ -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', () => {
|
||||
|
@ -38,6 +39,7 @@ describe('IP Overview Component', () => {
|
|||
}>,
|
||||
indexPatterns: [],
|
||||
jobNameById: {},
|
||||
scopeId: SourcererScopeName.default,
|
||||
};
|
||||
|
||||
test('it renders the default IP Overview', () => {
|
||||
|
|
|
@ -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;
|
||||
|
@ -51,6 +52,7 @@ export interface IpOverviewProps {
|
|||
isLoadingAnomaliesData: boolean;
|
||||
loading: boolean;
|
||||
narrowDateRange: NarrowDateRange;
|
||||
scopeId: SourcererScopeName;
|
||||
startDate: string;
|
||||
type: networkModel.NetworkType;
|
||||
indexPatterns: string[];
|
||||
|
@ -71,6 +73,7 @@ export const IpOverview = React.memo<IpOverviewProps>(
|
|||
isLoadingAnomaliesData,
|
||||
anomaliesData,
|
||||
narrowDateRange,
|
||||
scopeId,
|
||||
indexPatterns,
|
||||
jobNameById,
|
||||
}) => {
|
||||
|
@ -145,13 +148,20 @@ export const IpOverview = React.memo<IpOverviewProps>(
|
|||
title: i18n.HOST_ID,
|
||||
description:
|
||||
typeData && data.host
|
||||
? hostIdRenderer({ host: data.host, ipFilter: ip, contextID })
|
||||
? hostIdRenderer({
|
||||
host: data.host,
|
||||
ipFilter: ip,
|
||||
contextID,
|
||||
scopeId,
|
||||
})
|
||||
: getEmptyTagValue(),
|
||||
},
|
||||
{
|
||||
title: i18n.HOST_NAME,
|
||||
description:
|
||||
typeData && data.host ? hostNameRenderer(data.host, ip, contextID) : getEmptyTagValue(),
|
||||
typeData && data.host
|
||||
? hostNameRenderer(scopeId, data.host, ip, contextID)
|
||||
: getEmptyTagValue(),
|
||||
},
|
||||
],
|
||||
[
|
||||
|
|
|
@ -59,6 +59,7 @@ import {
|
|||
CellActionsMode,
|
||||
SecurityCellActionsTrigger,
|
||||
} from '../../../../common/components/cell_actions';
|
||||
import { SourcererScopeName } from '../../../../sourcerer/store/model';
|
||||
|
||||
const NetworkDetailsManage = manageQuery(IpOverview);
|
||||
|
||||
|
@ -224,6 +225,7 @@ const NetworkDetailsComponent: React.FC = () => {
|
|||
narrowDateRange={narrowDateRange}
|
||||
indexPatterns={selectedPatterns}
|
||||
jobNameById={jobNameById}
|
||||
scopeId={SourcererScopeName.default}
|
||||
/>
|
||||
|
||||
<EuiHorizontalRule />
|
||||
|
|
|
@ -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 {
|
||||
/**
|
||||
|
@ -116,6 +117,7 @@ export const NetworkDetails = ({ ip, flowTarget }: NetworkDetailsProps) => {
|
|||
narrowDateRange={narrowDateRange}
|
||||
indexPatterns={selectedPatterns}
|
||||
jobNameById={jobNameById}
|
||||
scopeId={SourcererScopeName.default}
|
||||
/>
|
||||
) : (
|
||||
<EmptyPrompt />
|
||||
|
|
|
@ -176,7 +176,7 @@ export const HostOverview = React.memo<HostSummaryProps>(
|
|||
title: i18n.HOST_ID,
|
||||
description:
|
||||
data && data.host
|
||||
? hostIdRenderer({ host: data.host, noLink: true })
|
||||
? hostIdRenderer({ host: data.host, noLink: true, scopeId })
|
||||
: getEmptyTagValue(),
|
||||
},
|
||||
{
|
||||
|
@ -202,7 +202,7 @@ export const HostOverview = React.memo<HostSummaryProps>(
|
|||
),
|
||||
},
|
||||
],
|
||||
[data, indexNames, hostName]
|
||||
[data, scopeId, indexNames, hostName]
|
||||
);
|
||||
const firstColumn = useMemo(
|
||||
() =>
|
||||
|
|
|
@ -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');
|
||||
|
@ -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(
|
||||
|
@ -104,24 +107,32 @@ 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();
|
||||
});
|
||||
});
|
||||
|
@ -129,7 +140,9 @@ describe('Field Renderers', () => {
|
|||
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();
|
||||
|
@ -137,21 +150,29 @@ describe('Field Renderers', () => {
|
|||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
@ -91,6 +92,7 @@ interface HostIdRendererTypes {
|
|||
host: HostEcs;
|
||||
ipFilter?: string;
|
||||
noLink?: boolean;
|
||||
scopeId: string | undefined;
|
||||
}
|
||||
|
||||
export const hostIdRenderer = ({
|
||||
|
@ -98,6 +100,7 @@ export const hostIdRenderer = ({
|
|||
host,
|
||||
ipFilter,
|
||||
noLink,
|
||||
scopeId,
|
||||
}: HostIdRendererTypes): React.ReactElement =>
|
||||
host.id && host.ip && (ipFilter == null || host.ip.includes(ipFilter)) ? (
|
||||
<>
|
||||
|
@ -110,6 +113,7 @@ export const hostIdRenderer = ({
|
|||
value={host.id[0]}
|
||||
isAggregatable={true}
|
||||
fieldType={'keyword'}
|
||||
scopeId={scopeId}
|
||||
>
|
||||
{noLink ? (
|
||||
<>{host.id}</>
|
||||
|
@ -126,6 +130,7 @@ export const hostIdRenderer = ({
|
|||
);
|
||||
|
||||
export const hostNameRenderer = (
|
||||
scopeId: SourcererScopeName,
|
||||
host?: HostEcs,
|
||||
ipFilter?: string,
|
||||
contextID?: string
|
||||
|
@ -143,6 +148,7 @@ export const hostNameRenderer = (
|
|||
value={host.name[0]}
|
||||
isAggregatable={true}
|
||||
fieldType={'keyword'}
|
||||
scopeId={scopeId}
|
||||
>
|
||||
<HostDetailsLink hostName={host.name[0]}>
|
||||
{host.name ? host.name : getEmptyTagValue()}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue