mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution][Investigations] - Use index alias in place of backing index (#138331)
This commit is contained in:
parent
c7ee95079c
commit
3ad0484d3e
9 changed files with 116 additions and 20 deletions
|
@ -9,7 +9,7 @@ import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
|||
import { OpenInDevConsoleButton } from '.';
|
||||
import { TestProviders } from '../../mock';
|
||||
|
||||
jest.mock('../../../risk_score/containers/common', () => ({
|
||||
jest.mock('../../hooks/use_space_id', () => ({
|
||||
useSpaceId: jest.fn().mockReturnValue('myspace'),
|
||||
}));
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { EuiButton, EuiFlexItem, EuiToolTip } from '@elastic/eui';
|
||||
import { useSpaceId } from '../../../risk_score/containers/common';
|
||||
import { useSpaceId } from '../../hooks/use_space_id';
|
||||
|
||||
interface OpenInDevConsoleButtonProps {
|
||||
enableButton: boolean;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useKibana } from '../../../common/lib/kibana';
|
||||
import { useKibana } from '../lib/kibana';
|
||||
|
||||
export const useSpaceId = () => {
|
||||
const { spaces } = useKibana().services;
|
|
@ -16,7 +16,7 @@ import { devToolPrebuiltContentUrl } from '../../../../common/constants';
|
|||
import { OpenInDevConsoleButton } from '../../../common/components/open_in_dev_console';
|
||||
import { useChcekSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_check_signal_index';
|
||||
import type { LinkPanelListItem } from '../link_panel';
|
||||
import { useSpaceId } from '../../../risk_score/containers/common';
|
||||
import { useSpaceId } from '../../../common/hooks/use_space_id';
|
||||
|
||||
export const RISKY_HOSTS_DOC_LINK =
|
||||
'https://www.github.com/elastic/detection-rules/blob/main/docs/experimental-machine-learning/host-risk-score.md';
|
||||
|
|
|
@ -34,7 +34,7 @@ import { useAppToasts } from '../../../common/hooks/use_app_toasts';
|
|||
import { isIndexNotFoundError } from '../../../common/utils/exceptions';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
|
||||
import type { inputsModel } from '../../../common/store';
|
||||
import { useSpaceId } from '../common';
|
||||
import { useSpaceId } from '../../../common/hooks/use_space_id';
|
||||
|
||||
export interface RiskScoreState<RiskScoreType extends HostsRiskScore[] | UsersRiskScore[]> {
|
||||
data?: RiskScoreType;
|
||||
|
|
|
@ -30,7 +30,7 @@ import { isIndexNotFoundError } from '../../../common/utils/exceptions';
|
|||
import type { ESTermQuery } from '../../../../common/typed_json';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
|
||||
import type { SeverityCount } from '../../../common/components/severity/types';
|
||||
import { useSpaceId } from '../common';
|
||||
import { useSpaceId } from '../../../common/hooks/use_space_id';
|
||||
|
||||
type GetHostsRiskScoreProps = KpiRiskScoreRequestOptions & {
|
||||
data: DataPublicPluginStart;
|
||||
|
|
|
@ -9,6 +9,7 @@ import { some } from 'lodash/fp';
|
|||
import { useMemo } from 'react';
|
||||
import type { TimelineEventsDetailsItem } from '../../../../../common/search_strategy';
|
||||
import { getFieldValue } from '../../../../detections/components/host_isolation/helpers';
|
||||
import { DEFAULT_ALERTS_INDEX, DEFAULT_PREVIEW_INDEX } from '../../../../../common/constants';
|
||||
|
||||
interface GetBasicDataFromDetailsData {
|
||||
alertId: string;
|
||||
|
@ -51,3 +52,20 @@ export const useBasicDataFromDetailsData = (
|
|||
[alertId, hostName, isAlert, ruleName, timestamp]
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
The referenced alert _index in the flyout uses the `.internal.` such as
|
||||
`.internal.alerts-security.alerts-spaceId` in the alert page flyout and
|
||||
.internal.preview.alerts-security.alerts-spaceId` in the rule creation preview flyout
|
||||
but we always want to use their respective aliase indices rather than accessing their backing .internal. indices.
|
||||
*/
|
||||
export const getAlertIndexAlias = (
|
||||
index: string,
|
||||
spaceId: string = 'default'
|
||||
): string | undefined => {
|
||||
if (index.startsWith(`.internal${DEFAULT_ALERTS_INDEX}`)) {
|
||||
return `${DEFAULT_ALERTS_INDEX}-${spaceId}`;
|
||||
} else if (index.startsWith(`.internal${DEFAULT_PREVIEW_INDEX}`)) {
|
||||
return `${DEFAULT_PREVIEW_INDEX}-${spaceId}`;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -11,8 +11,6 @@ import '../../../../common/mock/match_media';
|
|||
import { TestProviders } from '../../../../common/mock';
|
||||
import { TimelineId, TimelineTabs } from '../../../../../common/types/timeline';
|
||||
import type { Ecs } from '../../../../../common/ecs';
|
||||
import { mockAlertDetailsData } from '../../../../common/components/event_details/__mocks__';
|
||||
import type { TimelineEventsDetailsItem } from '../../../../../common/search_strategy';
|
||||
import {
|
||||
KibanaServices,
|
||||
useKibana,
|
||||
|
@ -21,7 +19,9 @@ import {
|
|||
import { mockBrowserFields, mockRuntimeMappings } from '../../../../common/containers/source/mock';
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
import { mockCasesContext } from '@kbn/cases-plugin/public/mocks/mock_cases_context';
|
||||
import { useTimelineEventsDetails } from '../../../containers/details';
|
||||
import { allCasesPermissions } from '../../../../cases_test_utils';
|
||||
import { DEFAULT_ALERTS_INDEX, DEFAULT_PREVIEW_INDEX } from '../../../../../common/constants';
|
||||
|
||||
const ecsData: Ecs = {
|
||||
_id: '1',
|
||||
|
@ -37,19 +37,16 @@ const ecsData: Ecs = {
|
|||
},
|
||||
};
|
||||
|
||||
const mockAlertDetailsDataWithIsObject = mockAlertDetailsData.map((detail) => {
|
||||
return {
|
||||
...detail,
|
||||
isObjectArray: false,
|
||||
};
|
||||
}) as TimelineEventsDetailsItem[];
|
||||
|
||||
jest.mock('../../../../../common/endpoint/service/host_isolation/utils', () => {
|
||||
return {
|
||||
isIsolationSupported: jest.fn().mockReturnValue(true),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../common/hooks/use_space_id', () => ({
|
||||
useSpaceId: jest.fn().mockReturnValue('testSpace'),
|
||||
}));
|
||||
|
||||
jest.mock(
|
||||
'../../../../detections/containers/detection_engine/alerts/use_host_isolation_status',
|
||||
() => {
|
||||
|
@ -101,18 +98,23 @@ const mockSearchStrategy = jest.fn();
|
|||
|
||||
const defaultProps = {
|
||||
timelineId: TimelineId.test,
|
||||
loadingEventDetails: false,
|
||||
detailsEcsData: ecsData,
|
||||
isHostIsolationPanelOpen: false,
|
||||
handleOnEventClosed: jest.fn(),
|
||||
onAddIsolationStatusClick: jest.fn(),
|
||||
expandedEvent: { eventId: ecsData._id, indexName: '' },
|
||||
detailsData: mockAlertDetailsDataWithIsObject,
|
||||
tabType: TimelineTabs.query,
|
||||
browserFields: mockBrowserFields,
|
||||
runtimeMappings: mockRuntimeMappings,
|
||||
};
|
||||
|
||||
jest.mock('../../../containers/details', () => {
|
||||
const actual = jest.requireActual('../../../containers/details');
|
||||
return {
|
||||
...actual,
|
||||
useTimelineEventsDetails: jest.fn().mockImplementation(() => []),
|
||||
};
|
||||
});
|
||||
|
||||
describe('event details footer component', () => {
|
||||
beforeEach(() => {
|
||||
const coreStartMock = coreMock.createStart();
|
||||
|
@ -169,4 +171,76 @@ describe('event details footer component', () => {
|
|||
const element = wrapper.queryByTestId('side-panel-flyout-footer');
|
||||
expect(element).toBeNull();
|
||||
});
|
||||
|
||||
describe('Alerts', () => {
|
||||
const propsWithAlertIndex = {
|
||||
...defaultProps,
|
||||
expandedEvent: {
|
||||
eventId: ecsData._id,
|
||||
indexName: `.internal${DEFAULT_ALERTS_INDEX}-testSpace`,
|
||||
},
|
||||
};
|
||||
test('it uses the alias alerts index', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<EventDetailsPanel {...{ ...propsWithAlertIndex }} />
|
||||
</TestProviders>
|
||||
);
|
||||
expect(useTimelineEventsDetails).toHaveBeenCalledWith({
|
||||
entityType: 'events',
|
||||
indexName: `${DEFAULT_ALERTS_INDEX}-testSpace`,
|
||||
eventId: propsWithAlertIndex.expandedEvent.eventId ?? '',
|
||||
runtimeMappings: mockRuntimeMappings,
|
||||
skip: false,
|
||||
});
|
||||
});
|
||||
|
||||
test('it uses the alias alerts preview index', () => {
|
||||
const alertPreviewProps = {
|
||||
...propsWithAlertIndex,
|
||||
expandedEvent: {
|
||||
...propsWithAlertIndex.expandedEvent,
|
||||
indexName: `.internal${DEFAULT_PREVIEW_INDEX}-testSpace`,
|
||||
},
|
||||
};
|
||||
render(
|
||||
<TestProviders>
|
||||
<EventDetailsPanel {...{ ...alertPreviewProps }} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(useTimelineEventsDetails).toHaveBeenCalledWith({
|
||||
entityType: 'events',
|
||||
indexName: `${DEFAULT_PREVIEW_INDEX}-testSpace`,
|
||||
eventId: propsWithAlertIndex.expandedEvent.eventId,
|
||||
runtimeMappings: mockRuntimeMappings,
|
||||
skip: false,
|
||||
});
|
||||
});
|
||||
|
||||
test(`it does NOT use the alerts alias when regular events happen to include a trailing '${DEFAULT_ALERTS_INDEX}' in the index name`, () => {
|
||||
const indexName = `.ds-logs-endpoint.alerts-default-2022.08.09-000001${DEFAULT_ALERTS_INDEX}`; // a regular event, that happens to include a trailing `.alerts-security.alerts`
|
||||
const propsWithEventIndex = {
|
||||
...defaultProps,
|
||||
expandedEvent: {
|
||||
eventId: ecsData._id,
|
||||
indexName,
|
||||
},
|
||||
};
|
||||
|
||||
render(
|
||||
<TestProviders>
|
||||
<EventDetailsPanel {...{ ...propsWithEventIndex }} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(useTimelineEventsDetails).toHaveBeenCalledWith({
|
||||
entityType: 'events',
|
||||
indexName, // <-- use the original index name, not the alerts alias
|
||||
eventId: propsWithEventIndex.expandedEvent.eventId,
|
||||
runtimeMappings: mockRuntimeMappings,
|
||||
skip: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,7 +20,8 @@ import type { HostRisk } from '../../../../risk_score/containers';
|
|||
import { useHostRiskScore } from '../../../../risk_score/containers';
|
||||
import { useHostIsolationTools } from './use_host_isolation_tools';
|
||||
import { FlyoutBody, FlyoutHeader, FlyoutFooter } from './flyout';
|
||||
import { useBasicDataFromDetailsData } from './helpers';
|
||||
import { useBasicDataFromDetailsData, getAlertIndexAlias } from './helpers';
|
||||
import { useSpaceId } from '../../../../common/hooks/use_space_id';
|
||||
|
||||
interface EventDetailsPanelProps {
|
||||
browserFields: BrowserFields;
|
||||
|
@ -51,10 +52,13 @@ const EventDetailsPanelComponent: React.FC<EventDetailsPanelProps> = ({
|
|||
timelineId,
|
||||
isReadOnly,
|
||||
}) => {
|
||||
const currentSpaceId = useSpaceId();
|
||||
const { indexName } = expandedEvent;
|
||||
const eventIndex = getAlertIndexAlias(indexName, currentSpaceId) ?? indexName;
|
||||
const [loading, detailsData, rawEventData, ecsData, refetchFlyoutData] = useTimelineEventsDetails(
|
||||
{
|
||||
entityType,
|
||||
indexName: expandedEvent.indexName ?? '',
|
||||
indexName: eventIndex ?? '',
|
||||
eventId: expandedEvent.eventId ?? '',
|
||||
runtimeMappings,
|
||||
skip: !expandedEvent.eventId,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue