mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
[Security Solution][Revisit external alerts] [2861] add alerts to explore event tabs and revamp network details (#136913)
* Remove External alert trend table and artifacts, and rename detection… (#136579) * Remove External alert trend table and artifacts, and rename detections alert * add test for SignasByCategory * Update signals_by_category.test.tsx * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' Co-authored-by: Kristof-Pierre Cummings <kristofpierre.cummings@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> * update event tab to show both alerts and events with toggle. (#136540) * add test for SignasByCategory * modify external_alerts_filter to be more efficient * Update usage across explore views to only use EventsQueryTabBody * remove unused files and code related to external alerts and move old alerts files to events_tab folder * test fixes, and more removal of old usage * update failing snapshots * last bit of cleanup * Fix type error * fix type and translations issue Co-authored-by: Kristof-Pierre Cummings <kristofpierre.cummings@elastic.co> * translations fixed * fix default stackBy value for alerts bug * memoizations added Co-authored-by: Kristof-Pierre Cummings <kristofpierre.cummings@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: semd <sergi.massaneda@elastic.co>
This commit is contained in:
parent
c2985c4daa
commit
3246ec733e
85 changed files with 563 additions and 1416 deletions
|
@ -99,7 +99,6 @@ export enum SecurityPageName {
|
|||
hostIsolationExceptions = 'host_isolation_exceptions',
|
||||
hosts = 'hosts',
|
||||
hostsAnomalies = 'hosts-anomalies',
|
||||
hostsExternalAlerts = 'hosts-external_alerts',
|
||||
hostsRisk = 'hosts-risk',
|
||||
hostsEvents = 'hosts-events',
|
||||
investigate = 'investigate',
|
||||
|
@ -124,11 +123,11 @@ export enum SecurityPageName {
|
|||
trustedApps = 'trusted_apps',
|
||||
uncommonProcesses = 'uncommon_processes',
|
||||
users = 'users',
|
||||
usersAuthentications = 'users-authentications',
|
||||
usersAnomalies = 'users-anomalies',
|
||||
usersRisk = 'users-risk',
|
||||
usersAuthentications = 'users-authentications',
|
||||
usersEvents = 'users-events',
|
||||
usersExternalAlerts = 'users-external_alerts',
|
||||
usersRisk = 'users-risk',
|
||||
}
|
||||
|
||||
export const EXPLORE_PATH = '/explore' as const;
|
||||
|
|
|
@ -311,13 +311,11 @@ export type TimelineWithoutExternalRefs = Omit<SavedTimeline, 'dataViewId' | 'sa
|
|||
|
||||
export enum TimelineId {
|
||||
usersPageEvents = 'users-page-events',
|
||||
usersPageExternalAlerts = 'users-page-external-alerts',
|
||||
hostsPageEvents = 'hosts-page-events',
|
||||
hostsPageExternalAlerts = 'hosts-page-external-alerts',
|
||||
networkPageEvents = 'network-page-events',
|
||||
hostsPageSessions = 'hosts-page-sessions-v2', // the v2 is to cache bust localstorage settings as default columns were reworked.
|
||||
detectionsRulesDetailsPage = 'detections-rules-details-page',
|
||||
detectionsPage = 'detections-page',
|
||||
networkPageExternalAlerts = 'network-page-external-alerts',
|
||||
active = 'timeline-1',
|
||||
casePage = 'timeline-case',
|
||||
test = 'test', // Reserved for testing purposes
|
||||
|
@ -328,13 +326,11 @@ export enum TimelineId {
|
|||
|
||||
export const TimelineIdLiteralRt = runtimeTypes.union([
|
||||
runtimeTypes.literal(TimelineId.usersPageEvents),
|
||||
runtimeTypes.literal(TimelineId.usersPageExternalAlerts),
|
||||
runtimeTypes.literal(TimelineId.hostsPageEvents),
|
||||
runtimeTypes.literal(TimelineId.hostsPageExternalAlerts),
|
||||
runtimeTypes.literal(TimelineId.networkPageEvents),
|
||||
runtimeTypes.literal(TimelineId.hostsPageSessions),
|
||||
runtimeTypes.literal(TimelineId.detectionsRulesDetailsPage),
|
||||
runtimeTypes.literal(TimelineId.detectionsPage),
|
||||
runtimeTypes.literal(TimelineId.networkPageExternalAlerts),
|
||||
runtimeTypes.literal(TimelineId.active),
|
||||
runtimeTypes.literal(TimelineId.test),
|
||||
runtimeTypes.literal(TimelineId.rulePreview),
|
||||
|
|
|
@ -12,10 +12,6 @@ import {
|
|||
AUTHENTICATIONS_TABLE,
|
||||
} from '../../screens/users/user_authentications';
|
||||
import { EVENTS_TAB, EVENTS_TAB_CONTENT } from '../../screens/users/user_events';
|
||||
import {
|
||||
EXTERNAL_ALERTS_TAB,
|
||||
EXTERNAL_ALERTS_TAB_CONTENT,
|
||||
} from '../../screens/users/user_external_alerts';
|
||||
import { RISK_SCORE_TAB, RISK_SCORE_TAB_CONTENT } from '../../screens/users/user_risk_score';
|
||||
import { cleanKibana } from '../../tasks/common';
|
||||
import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver';
|
||||
|
@ -66,12 +62,6 @@ describe('Users stats and tables', () => {
|
|||
cy.get(EVENTS_TAB_CONTENT).should('exist');
|
||||
});
|
||||
|
||||
it(`renders external alerts tab`, () => {
|
||||
cy.get(EXTERNAL_ALERTS_TAB).click({ force: true });
|
||||
|
||||
cy.get(EXTERNAL_ALERTS_TAB_CONTENT).should('exist');
|
||||
});
|
||||
|
||||
it(`renders users risk tab`, () => {
|
||||
cy.get(RISK_SCORE_TAB).click({ force: true });
|
||||
|
||||
|
|
|
@ -265,13 +265,6 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [
|
|||
}),
|
||||
path: `${HOSTS_PATH}/events`,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.hostsExternalAlerts,
|
||||
title: i18n.translate('xpack.securitySolution.search.hosts.externalAlerts', {
|
||||
defaultMessage: 'External Alerts',
|
||||
}),
|
||||
path: `${HOSTS_PATH}/externalAlerts`,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.hostsRisk,
|
||||
title: i18n.translate('xpack.securitySolution.search.hosts.risk', {
|
||||
|
@ -320,13 +313,6 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [
|
|||
}),
|
||||
path: `${NETWORK_PATH}/tls`,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.networkExternalAlerts,
|
||||
title: i18n.translate('xpack.securitySolution.search.network.externalAlerts', {
|
||||
defaultMessage: 'External Alerts',
|
||||
}),
|
||||
path: `${NETWORK_PATH}/external-alerts`,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.networkAnomalies,
|
||||
title: i18n.translate('xpack.securitySolution.search.hosts.anomalies', {
|
||||
|
@ -377,13 +363,6 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [
|
|||
}),
|
||||
path: `${USERS_PATH}/events`,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.usersExternalAlerts,
|
||||
title: i18n.translate('xpack.securitySolution.search.users.externalAlerts', {
|
||||
defaultMessage: 'External Alerts',
|
||||
}),
|
||||
path: `${USERS_PATH}/externalAlerts`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
* 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, { useEffect, useMemo } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import type { Filter } from '@kbn/es-query';
|
||||
import type { EntityType } from '@kbn/timelines-plugin/common';
|
||||
import { timelineActions } from '../../../timelines/store/timeline';
|
||||
import type { TimelineIdLiteral } from '../../../../common/types/timeline';
|
||||
import { StatefulEventsViewer } from '../events_viewer';
|
||||
import { alertsDefaultModel } from './default_headers';
|
||||
import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
|
||||
import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer';
|
||||
import * as i18n from './translations';
|
||||
import { defaultCellActions } from '../../lib/cell_actions/default_cell_actions';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
import { SourcererScopeName } from '../../store/sourcerer/model';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';
|
||||
import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants';
|
||||
import { getDefaultControlColumn } from '../../../timelines/components/timeline/body/control_columns';
|
||||
|
||||
export interface OwnProps {
|
||||
end: string;
|
||||
id: string;
|
||||
start: string;
|
||||
}
|
||||
|
||||
const defaultAlertsFilters: Filter[] = [
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
negate: false,
|
||||
disabled: false,
|
||||
type: 'phrase',
|
||||
key: 'event.kind',
|
||||
params: {
|
||||
query: 'alert',
|
||||
},
|
||||
},
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
match: {
|
||||
'event.kind': 'alert',
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
interface Props {
|
||||
timelineId: TimelineIdLiteral;
|
||||
endDate: string;
|
||||
entityType?: EntityType;
|
||||
startDate: string;
|
||||
pageFilters?: Filter[];
|
||||
}
|
||||
|
||||
const AlertsTableComponent: React.FC<Props> = ({
|
||||
timelineId,
|
||||
endDate,
|
||||
entityType = 'alerts',
|
||||
startDate,
|
||||
pageFilters = [],
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const alertsFilter = useMemo(() => [...defaultAlertsFilters, ...pageFilters], [pageFilters]);
|
||||
const { filterManager } = useKibana().services.data.query;
|
||||
const ACTION_BUTTON_COUNT = 5;
|
||||
|
||||
const tGridEnabled = useIsExperimentalFeatureEnabled('tGridEnabled');
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
timelineActions.initializeTGridSettings({
|
||||
id: timelineId,
|
||||
documentType: i18n.ALERTS_DOCUMENT_TYPE,
|
||||
filterManager,
|
||||
defaultColumns: alertsDefaultModel.columns.map((c) =>
|
||||
!tGridEnabled && c.initialWidth == null
|
||||
? {
|
||||
...c,
|
||||
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
|
||||
}
|
||||
: c
|
||||
),
|
||||
excludedRowRendererIds: alertsDefaultModel.excludedRowRendererIds,
|
||||
footerText: i18n.TOTAL_COUNT_OF_ALERTS,
|
||||
title: i18n.ALERTS_TABLE_TITLE,
|
||||
// TODO: avoid passing this through the store
|
||||
})
|
||||
);
|
||||
}, [dispatch, filterManager, tGridEnabled, timelineId]);
|
||||
|
||||
const leadingControlColumns = useMemo(() => getDefaultControlColumn(ACTION_BUTTON_COUNT), []);
|
||||
|
||||
return (
|
||||
<StatefulEventsViewer
|
||||
pageFilters={alertsFilter}
|
||||
defaultModel={alertsDefaultModel}
|
||||
defaultCellActions={defaultCellActions}
|
||||
end={endDate}
|
||||
entityType={entityType}
|
||||
id={timelineId}
|
||||
leadingControlColumns={leadingControlColumns}
|
||||
renderCellValue={DefaultCellRenderer}
|
||||
rowRenderers={defaultRowRenderers}
|
||||
scopeId={SourcererScopeName.default}
|
||||
start={startDate}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const AlertsTable = React.memo(AlertsTableComponent);
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* 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 * as i18n from './translations';
|
||||
import type { MatrixHistogramOption, MatrixHistogramConfigs } from '../matrix_histogram/types';
|
||||
import { MatrixHistogramType } from '../../../../common/search_strategy/security_solution/matrix_histogram';
|
||||
import { getExternalAlertLensAttributes } from '../visualization_actions/lens_attributes/common/external_alert';
|
||||
|
||||
export const alertsStackByOptions: MatrixHistogramOption[] = [
|
||||
{
|
||||
text: 'event.category',
|
||||
value: 'event.category',
|
||||
},
|
||||
{
|
||||
text: 'event.module',
|
||||
value: 'event.module',
|
||||
},
|
||||
];
|
||||
|
||||
const DEFAULT_STACK_BY = 'event.module';
|
||||
|
||||
export const histogramConfigs: MatrixHistogramConfigs = {
|
||||
defaultStackByOption:
|
||||
alertsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ?? alertsStackByOptions[1],
|
||||
errorMessage: i18n.ERROR_FETCHING_ALERTS_DATA,
|
||||
histogramType: MatrixHistogramType.alerts,
|
||||
stackByOptions: alertsStackByOptions,
|
||||
subtitle: undefined,
|
||||
title: i18n.ALERTS_GRAPH_TITLE,
|
||||
getLensAttributes: getExternalAlertLensAttributes,
|
||||
};
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* 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, { useEffect, useCallback, useMemo } from 'react';
|
||||
import numeral from '@elastic/numeral';
|
||||
|
||||
import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants';
|
||||
import { useGlobalFullScreen } from '../../containers/use_full_screen';
|
||||
|
||||
import type { AlertsComponentsProps } from './types';
|
||||
import { AlertsTable } from './alerts_table';
|
||||
import * as i18n from './translations';
|
||||
import { useUiSetting$ } from '../../lib/kibana';
|
||||
import { MatrixHistogram } from '../matrix_histogram';
|
||||
import { histogramConfigs } from './histogram_configs';
|
||||
import type { MatrixHistogramConfigs } from '../matrix_histogram/types';
|
||||
|
||||
const ID = 'alertsHistogramQuery';
|
||||
|
||||
const AlertsViewComponent: React.FC<AlertsComponentsProps> = ({
|
||||
timelineId,
|
||||
deleteQuery,
|
||||
endDate,
|
||||
entityType,
|
||||
filterQuery,
|
||||
indexNames,
|
||||
pageFilters,
|
||||
setQuery,
|
||||
startDate,
|
||||
}) => {
|
||||
const [defaultNumberFormat] = useUiSetting$<string>(DEFAULT_NUMBER_FORMAT);
|
||||
const { globalFullScreen } = useGlobalFullScreen();
|
||||
|
||||
const getSubtitle = useCallback(
|
||||
(totalCount: number) =>
|
||||
`${i18n.SHOWING}: ${numeral(totalCount).format(defaultNumberFormat)} ${i18n.UNIT(
|
||||
totalCount
|
||||
)}`,
|
||||
[defaultNumberFormat]
|
||||
);
|
||||
|
||||
const alertsHistogramConfigs: MatrixHistogramConfigs = useMemo(
|
||||
() => ({
|
||||
...histogramConfigs,
|
||||
subtitle: getSubtitle,
|
||||
}),
|
||||
[getSubtitle]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (deleteQuery) {
|
||||
deleteQuery({ id: ID });
|
||||
}
|
||||
};
|
||||
}, [deleteQuery]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!globalFullScreen && (
|
||||
<MatrixHistogram
|
||||
endDate={endDate}
|
||||
filterQuery={filterQuery}
|
||||
id={ID}
|
||||
indexNames={indexNames}
|
||||
setQuery={setQuery}
|
||||
startDate={startDate}
|
||||
{...alertsHistogramConfigs}
|
||||
/>
|
||||
)}
|
||||
<AlertsTable
|
||||
timelineId={timelineId}
|
||||
endDate={endDate}
|
||||
entityType={entityType}
|
||||
startDate={startDate}
|
||||
pageFilters={pageFilters}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
AlertsViewComponent.displayName = 'AlertsViewComponent';
|
||||
|
||||
export const AlertsView = React.memo(AlertsViewComponent);
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
|
||||
export const ALERTS_DOCUMENT_TYPE = i18n.translate(
|
||||
'xpack.securitySolution.alertsView.alertsDocumentType',
|
||||
{
|
||||
defaultMessage: 'External alerts',
|
||||
}
|
||||
);
|
||||
|
||||
export const TOTAL_COUNT_OF_ALERTS = i18n.translate(
|
||||
'xpack.securitySolution.alertsView.totalCountOfAlerts',
|
||||
{
|
||||
defaultMessage: 'external alerts',
|
||||
}
|
||||
);
|
||||
|
||||
export const ALERTS_TABLE_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.alertsView.alertsTableTitle',
|
||||
{
|
||||
defaultMessage: 'External alerts',
|
||||
}
|
||||
);
|
||||
|
||||
export const ALERTS_GRAPH_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.alertsView.alertsGraphTitle',
|
||||
{
|
||||
defaultMessage: 'External alert trend',
|
||||
}
|
||||
);
|
||||
|
||||
export const SHOWING = i18n.translate('xpack.securitySolution.alertsView.showing', {
|
||||
defaultMessage: 'Showing',
|
||||
});
|
||||
|
||||
export const UNIT = (totalCount: number) =>
|
||||
i18n.translate('xpack.securitySolution.alertsView.unit', {
|
||||
values: { totalCount },
|
||||
defaultMessage: `external {totalCount, plural, =1 {alert} other {alerts}}`,
|
||||
});
|
||||
|
||||
export const ERROR_FETCHING_ALERTS_DATA = i18n.translate(
|
||||
'xpack.securitySolution.alertsView.errorFetchingAlertsData',
|
||||
{
|
||||
defaultMessage: 'Failed to query alerts data',
|
||||
}
|
||||
);
|
||||
|
||||
export const CATEGORY = i18n.translate('xpack.securitySolution.alertsView.categoryLabel', {
|
||||
defaultMessage: 'category',
|
||||
});
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* 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 type { Filter } from '@kbn/es-query';
|
||||
import type { EntityType } from '@kbn/timelines-plugin/common';
|
||||
import type { TimelineIdLiteral } from '../../../../common/types/timeline';
|
||||
import type { HostsComponentsQueryProps } from '../../../hosts/pages/navigation/types';
|
||||
import type { NetworkComponentQueryProps } from '../../../network/pages/navigation/types';
|
||||
import type { MatrixHistogramOption } from '../matrix_histogram/types';
|
||||
|
||||
type CommonQueryProps = HostsComponentsQueryProps | NetworkComponentQueryProps;
|
||||
|
||||
export interface AlertsComponentsProps
|
||||
extends Pick<
|
||||
CommonQueryProps,
|
||||
'deleteQuery' | 'endDate' | 'filterQuery' | 'skip' | 'setQuery' | 'startDate'
|
||||
> {
|
||||
timelineId: TimelineIdLiteral;
|
||||
pageFilters: Filter[];
|
||||
stackByOptions?: MatrixHistogramOption[];
|
||||
defaultFilters?: Filter[];
|
||||
defaultStackByOption?: MatrixHistogramOption;
|
||||
entityType?: EntityType;
|
||||
indexNames: string[];
|
||||
}
|
|
@ -42,7 +42,7 @@ import {
|
|||
import { useDeepEqualSelector } from '../../hooks/use_selector';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
|
||||
import { alertsHeaders } from '../alerts_viewer/default_headers';
|
||||
import { defaultAlertsHeaders } from '../events_viewer/default_alert_headers';
|
||||
|
||||
// @ts-expect-error
|
||||
window['__react-beautiful-dnd-disable-dev-warnings'] = true;
|
||||
|
@ -89,7 +89,7 @@ const onDragEndHandler = ({
|
|||
} else if (fieldWasDroppedOnTimelineColumns(result)) {
|
||||
addFieldToTimelineColumns({
|
||||
browserFields,
|
||||
defaultsHeader: alertsHeaders,
|
||||
defaultsHeader: defaultAlertsHeaders,
|
||||
dispatch,
|
||||
result,
|
||||
timelineId: getTimelineIdFromColumnDroppableId(result.destination?.droppableId ?? ''),
|
||||
|
|
|
@ -11,7 +11,7 @@ import { TimelineId } from '../../../../common/types';
|
|||
import { HostsType } from '../../../hosts/store/model';
|
||||
import { TestProviders } from '../../mock';
|
||||
import type { EventsQueryTabBodyComponentProps } from './events_query_tab_body';
|
||||
import { EventsQueryTabBody } from './events_query_tab_body';
|
||||
import { EventsQueryTabBody, ALERTS_EVENTS_HISTOGRAM_ID } from './events_query_tab_body';
|
||||
import { useGlobalFullScreen } from '../../containers/use_full_screen';
|
||||
import * as tGridActions from '@kbn/timelines-plugin/public/store/t_grid/actions';
|
||||
|
||||
|
@ -33,12 +33,17 @@ jest.mock('../../lib/kibana', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const FakeStatefulEventsViewer = () => <div>{'MockedStatefulEventsViewer'}</div>;
|
||||
const FakeStatefulEventsViewer = ({ additionalFilters }: { additionalFilters: JSX.Element }) => (
|
||||
<div>
|
||||
{additionalFilters}
|
||||
{'MockedStatefulEventsViewer'}
|
||||
</div>
|
||||
);
|
||||
jest.mock('../events_viewer', () => ({ StatefulEventsViewer: FakeStatefulEventsViewer }));
|
||||
|
||||
jest.mock('../../containers/use_full_screen', () => ({
|
||||
useGlobalFullScreen: jest.fn().mockReturnValue({
|
||||
globalFullScreen: true,
|
||||
globalFullScreen: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
|
@ -52,6 +57,10 @@ describe('EventsQueryTabBody', () => {
|
|||
startDate: new Date('2000').toISOString(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders EventsViewer', () => {
|
||||
const { queryByText } = render(
|
||||
<TestProviders>
|
||||
|
@ -63,7 +72,7 @@ describe('EventsQueryTabBody', () => {
|
|||
});
|
||||
|
||||
it('renders the matrix histogram when globalFullScreen is false', () => {
|
||||
(useGlobalFullScreen as jest.Mock).mockReturnValue({
|
||||
(useGlobalFullScreen as jest.Mock).mockReturnValueOnce({
|
||||
globalFullScreen: false,
|
||||
});
|
||||
|
||||
|
@ -73,11 +82,11 @@ describe('EventsQueryTabBody', () => {
|
|||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(queryByTestId('eventsHistogramQueryPanel')).toBeInTheDocument();
|
||||
expect(queryByTestId(`${ALERTS_EVENTS_HISTOGRAM_ID}Panel`)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("doesn't render the matrix histogram when globalFullScreen is true", () => {
|
||||
(useGlobalFullScreen as jest.Mock).mockReturnValue({
|
||||
(useGlobalFullScreen as jest.Mock).mockReturnValueOnce({
|
||||
globalFullScreen: true,
|
||||
});
|
||||
|
||||
|
@ -87,7 +96,33 @@ describe('EventsQueryTabBody', () => {
|
|||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(queryByTestId('eventsHistogramQueryPanel')).not.toBeInTheDocument();
|
||||
expect(queryByTestId(`${ALERTS_EVENTS_HISTOGRAM_ID}Panel`)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the matrix histogram stacked by events default value', () => {
|
||||
const result = render(
|
||||
<TestProviders>
|
||||
<EventsQueryTabBody {...commonProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(result.getByTestId('header-section-supplements').querySelector('select')?.value).toEqual(
|
||||
'event.action'
|
||||
);
|
||||
});
|
||||
|
||||
it('renders the matrix histogram stacked by alerts default value', () => {
|
||||
const result = render(
|
||||
<TestProviders>
|
||||
<EventsQueryTabBody {...commonProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
result.getByTestId('showExternalAlertsCheckbox').click();
|
||||
|
||||
expect(result.getByTestId('header-section-supplements').querySelector('select')?.value).toEqual(
|
||||
'event.module'
|
||||
);
|
||||
});
|
||||
|
||||
it('deletes query when unmouting', () => {
|
||||
|
|
|
@ -5,69 +5,52 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { EuiCheckbox } from '@elastic/eui';
|
||||
import type { Filter } from '@kbn/es-query';
|
||||
import type { EntityType } from '@kbn/timelines-plugin/common';
|
||||
|
||||
import type { TimelineId } from '../../../../common/types/timeline';
|
||||
import { RowRendererId } from '../../../../common/types/timeline';
|
||||
import { StatefulEventsViewer } from '../events_viewer';
|
||||
import { timelineActions } from '../../../timelines/store/timeline';
|
||||
import { eventsDefaultModel } from '../events_viewer/default_model';
|
||||
|
||||
import { MatrixHistogram } from '../matrix_histogram';
|
||||
import { useGlobalFullScreen } from '../../containers/use_full_screen';
|
||||
import * as i18n from '../../../hosts/pages/translations';
|
||||
import { MatrixHistogramType } from '../../../../common/search_strategy/security_solution';
|
||||
import * as i18n from './translations';
|
||||
import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants';
|
||||
import {
|
||||
alertsHistogramConfig,
|
||||
eventsHistogramConfig,
|
||||
getSubtitleFunction,
|
||||
} from './histogram_configurations';
|
||||
import { getDefaultControlColumn } from '../../../timelines/components/timeline/body/control_columns';
|
||||
import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
|
||||
import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer';
|
||||
import { SourcererScopeName } from '../../store/sourcerer/model';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';
|
||||
import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants';
|
||||
import { getEventsHistogramLensAttributes } from '../visualization_actions/lens_attributes/hosts/events';
|
||||
import { defaultCellActions } from '../../lib/cell_actions/default_cell_actions';
|
||||
import type { GlobalTimeArgs } from '../../containers/use_global_time';
|
||||
import type { MatrixHistogramConfigs, MatrixHistogramOption } from '../matrix_histogram/types';
|
||||
import type { QueryTabBodyProps as UserQueryTabBodyProps } from '../../../users/pages/navigation/types';
|
||||
import type { QueryTabBodyProps as HostQueryTabBodyProps } from '../../../hosts/pages/navigation/types';
|
||||
import type { QueryTabBodyProps as NetworkQueryTabBodyProps } from '../../../network/pages/navigation/types';
|
||||
|
||||
const EVENTS_HISTOGRAM_ID = 'eventsHistogramQuery';
|
||||
import { useUiSetting$ } from '../../lib/kibana';
|
||||
import { defaultAlertsFilters } from '../events_viewer/external_alerts_filter';
|
||||
|
||||
export const eventsStackByOptions: MatrixHistogramOption[] = [
|
||||
{
|
||||
text: 'event.action',
|
||||
value: 'event.action',
|
||||
},
|
||||
{
|
||||
text: 'event.dataset',
|
||||
value: 'event.dataset',
|
||||
},
|
||||
{
|
||||
text: 'event.module',
|
||||
value: 'event.module',
|
||||
},
|
||||
];
|
||||
const ACTION_BUTTON_COUNT = 5;
|
||||
export const ALERTS_EVENTS_HISTOGRAM_ID = 'alertsOrEventsHistogramQuery';
|
||||
|
||||
const DEFAULT_STACK_BY = 'event.action';
|
||||
const unit = (n: number) => i18n.EVENTS_UNIT(n);
|
||||
|
||||
export const histogramConfigs: MatrixHistogramConfigs = {
|
||||
defaultStackByOption:
|
||||
eventsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ?? eventsStackByOptions[0],
|
||||
errorMessage: i18n.ERROR_FETCHING_EVENTS_DATA,
|
||||
histogramType: MatrixHistogramType.events,
|
||||
stackByOptions: eventsStackByOptions,
|
||||
subtitle: undefined,
|
||||
title: i18n.NAVIGATION_EVENTS_TITLE,
|
||||
getLensAttributes: getEventsHistogramLensAttributes,
|
||||
};
|
||||
|
||||
type QueryTabBodyProps = UserQueryTabBodyProps | HostQueryTabBodyProps;
|
||||
type QueryTabBodyProps = UserQueryTabBodyProps | HostQueryTabBodyProps | NetworkQueryTabBodyProps;
|
||||
|
||||
export type EventsQueryTabBodyComponentProps = QueryTabBodyProps & {
|
||||
deleteQuery?: GlobalTimeArgs['deleteQuery'];
|
||||
indexNames: string[];
|
||||
pageFilters?: Filter[];
|
||||
externalAlertPageFilters?: Filter[];
|
||||
setQuery: GlobalTimeArgs['setQuery'];
|
||||
timelineId: TimelineId;
|
||||
};
|
||||
|
@ -77,15 +60,24 @@ const EventsQueryTabBodyComponent: React.FC<EventsQueryTabBodyComponentProps> =
|
|||
endDate,
|
||||
filterQuery,
|
||||
indexNames,
|
||||
pageFilters,
|
||||
externalAlertPageFilters = [],
|
||||
pageFilters = [],
|
||||
setQuery,
|
||||
startDate,
|
||||
timelineId,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const { globalFullScreen } = useGlobalFullScreen();
|
||||
const ACTION_BUTTON_COUNT = 5;
|
||||
const tGridEnabled = useIsExperimentalFeatureEnabled('tGridEnabled');
|
||||
const [defaultNumberFormat] = useUiSetting$<string>(DEFAULT_NUMBER_FORMAT);
|
||||
const [showExternalAlerts, setShowExternalAlerts] = useState(false);
|
||||
const leadingControlColumns = useMemo(() => getDefaultControlColumn(ACTION_BUTTON_COUNT), []);
|
||||
|
||||
const toggleExternalAlerts = useCallback(() => setShowExternalAlerts((s) => !s), []);
|
||||
const getHistogramSubtitle = useMemo(
|
||||
() => getSubtitleFunction(defaultNumberFormat, showExternalAlerts),
|
||||
[defaultNumberFormat, showExternalAlerts]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
|
@ -99,46 +91,78 @@ const EventsQueryTabBodyComponent: React.FC<EventsQueryTabBodyComponentProps> =
|
|||
}
|
||||
: c
|
||||
),
|
||||
excludedRowRendererIds: showExternalAlerts ? Object.values(RowRendererId) : undefined,
|
||||
})
|
||||
);
|
||||
}, [dispatch, tGridEnabled, timelineId]);
|
||||
}, [dispatch, showExternalAlerts, tGridEnabled, timelineId]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (deleteQuery) {
|
||||
deleteQuery({ id: EVENTS_HISTOGRAM_ID });
|
||||
deleteQuery({ id: ALERTS_EVENTS_HISTOGRAM_ID });
|
||||
}
|
||||
};
|
||||
}, [deleteQuery]);
|
||||
|
||||
const leadingControlColumns = useMemo(() => getDefaultControlColumn(ACTION_BUTTON_COUNT), []);
|
||||
const additionalFilters = useMemo(
|
||||
() => (
|
||||
<EuiCheckbox
|
||||
id="showExternalAlertsCheckbox"
|
||||
data-test-subj="showExternalAlertsCheckbox"
|
||||
aria-label={i18n.SHOW_EXTERNAL_ALERTS}
|
||||
checked={showExternalAlerts}
|
||||
color="text"
|
||||
label={i18n.SHOW_EXTERNAL_ALERTS}
|
||||
onChange={toggleExternalAlerts}
|
||||
/>
|
||||
),
|
||||
[showExternalAlerts, toggleExternalAlerts]
|
||||
);
|
||||
|
||||
const defaultModel = useMemo(
|
||||
() => ({
|
||||
...eventsDefaultModel,
|
||||
excludedRowRendererIds: showExternalAlerts ? Object.values(RowRendererId) : [],
|
||||
}),
|
||||
[showExternalAlerts]
|
||||
);
|
||||
|
||||
const composedPageFilters = useMemo(
|
||||
() => [
|
||||
...pageFilters,
|
||||
...(showExternalAlerts ? [defaultAlertsFilters, ...externalAlertPageFilters] : []),
|
||||
],
|
||||
[showExternalAlerts, externalAlertPageFilters, pageFilters]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!globalFullScreen && (
|
||||
<MatrixHistogram
|
||||
id={ALERTS_EVENTS_HISTOGRAM_ID}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
filterQuery={filterQuery}
|
||||
setQuery={setQuery}
|
||||
startDate={startDate}
|
||||
id={EVENTS_HISTOGRAM_ID}
|
||||
indexNames={indexNames}
|
||||
{...histogramConfigs}
|
||||
setQuery={setQuery}
|
||||
{...(showExternalAlerts ? alertsHistogramConfig : eventsHistogramConfig)}
|
||||
subtitle={getHistogramSubtitle}
|
||||
/>
|
||||
)}
|
||||
<StatefulEventsViewer
|
||||
additionalFilters={additionalFilters}
|
||||
defaultCellActions={defaultCellActions}
|
||||
defaultModel={eventsDefaultModel}
|
||||
start={startDate}
|
||||
end={endDate}
|
||||
entityType="events"
|
||||
id={timelineId}
|
||||
entityType={'events' as EntityType}
|
||||
leadingControlColumns={leadingControlColumns}
|
||||
pageFilters={pageFilters}
|
||||
renderCellValue={DefaultCellRenderer}
|
||||
rowRenderers={defaultRowRenderers}
|
||||
scopeId={SourcererScopeName.default}
|
||||
start={startDate}
|
||||
unit={unit}
|
||||
id={timelineId}
|
||||
unit={showExternalAlerts ? i18n.ALERTS_UNIT : i18n.EVENTS_UNIT}
|
||||
defaultModel={defaultModel}
|
||||
pageFilters={composedPageFilters}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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 numeral from '@elastic/numeral';
|
||||
|
||||
import { MatrixHistogramType } from '../../../../common/search_strategy/security_solution';
|
||||
import { getExternalAlertLensAttributes } from '../visualization_actions/lens_attributes/common/external_alert';
|
||||
import { getEventsHistogramLensAttributes } from '../visualization_actions/lens_attributes/hosts/events';
|
||||
import type { MatrixHistogramConfigs, MatrixHistogramOption } from '../matrix_histogram/types';
|
||||
import * as i18n from './translations';
|
||||
|
||||
const DEFAULT_EVENTS_STACK_BY = 'event.action';
|
||||
|
||||
export const getSubtitleFunction =
|
||||
(defaultNumberFormat: string, isAlert: boolean) => (totalCount: number) =>
|
||||
`${i18n.SHOWING}: ${numeral(totalCount).format(defaultNumberFormat)} ${
|
||||
isAlert ? i18n.ALERTS_UNIT(totalCount) : i18n.EVENTS_UNIT(totalCount)
|
||||
}`;
|
||||
|
||||
export const eventsStackByOptions: MatrixHistogramOption[] = [
|
||||
{
|
||||
text: 'event.action',
|
||||
value: 'event.action',
|
||||
},
|
||||
{
|
||||
text: 'event.dataset',
|
||||
value: 'event.dataset',
|
||||
},
|
||||
{
|
||||
text: 'event.module',
|
||||
value: 'event.module',
|
||||
},
|
||||
];
|
||||
|
||||
export const eventsHistogramConfig: MatrixHistogramConfigs = {
|
||||
defaultStackByOption:
|
||||
eventsStackByOptions.find((o) => o.text === DEFAULT_EVENTS_STACK_BY) ?? eventsStackByOptions[0],
|
||||
errorMessage: i18n.ERROR_FETCHING_EVENTS_DATA,
|
||||
histogramType: MatrixHistogramType.events,
|
||||
stackByOptions: eventsStackByOptions,
|
||||
subtitle: undefined,
|
||||
title: i18n.EVENTS_GRAPH_TITLE,
|
||||
getLensAttributes: getEventsHistogramLensAttributes,
|
||||
};
|
||||
|
||||
export const alertsStackByOptions: MatrixHistogramOption[] = [
|
||||
{
|
||||
text: 'event.category',
|
||||
value: 'event.category',
|
||||
},
|
||||
{
|
||||
text: 'event.module',
|
||||
value: 'event.module',
|
||||
},
|
||||
];
|
||||
|
||||
const DEFAULT_STACK_BY = 'event.module';
|
||||
|
||||
export const alertsHistogramConfig: MatrixHistogramConfigs = {
|
||||
defaultStackByOption:
|
||||
alertsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ?? alertsStackByOptions[0],
|
||||
errorMessage: i18n.ERROR_FETCHING_ALERTS_DATA,
|
||||
histogramType: MatrixHistogramType.alerts,
|
||||
stackByOptions: alertsStackByOptions,
|
||||
subtitle: undefined,
|
||||
title: i18n.ALERTS_GRAPH_TITLE,
|
||||
getLensAttributes: getExternalAlertLensAttributes,
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { EventsQueryTabBody } from './events_query_tab_body';
|
||||
export {
|
||||
alertsHistogramConfig,
|
||||
alertsStackByOptions,
|
||||
eventsHistogramConfig,
|
||||
eventsStackByOptions,
|
||||
} from './histogram_configurations';
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
|
||||
export const ALERTS_UNIT = (totalCount: number) =>
|
||||
i18n.translate('xpack.securitySolution.eventsTab.unit', {
|
||||
values: { totalCount },
|
||||
defaultMessage: `external {totalCount, plural, =1 {alert} other {alerts}}`,
|
||||
});
|
||||
|
||||
export const EVENTS_UNIT = (totalCount: number) =>
|
||||
i18n.translate('xpack.securitySolution.hosts.navigaton.eventsUnit', {
|
||||
values: { totalCount },
|
||||
defaultMessage: `{totalCount, plural, =1 {event} other {events}}`,
|
||||
});
|
||||
|
||||
export const SHOWING = i18n.translate('xpack.securitySolution.eventsTab.showing', {
|
||||
defaultMessage: 'Showing',
|
||||
});
|
||||
|
||||
export const ALERTS_GRAPH_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.eventsTab.alertsGraphTitle',
|
||||
{
|
||||
defaultMessage: 'External alert trend',
|
||||
}
|
||||
);
|
||||
|
||||
export const ERROR_FETCHING_ALERTS_DATA = i18n.translate(
|
||||
'xpack.securitySolution.eventsTab.errorFetchingAlertsData',
|
||||
{
|
||||
defaultMessage: 'Failed to query alerts data',
|
||||
}
|
||||
);
|
||||
|
||||
export const ERROR_FETCHING_EVENTS_DATA = i18n.translate(
|
||||
'xpack.securitySolution.eventsTab.errorFetchingEventsData',
|
||||
{
|
||||
defaultMessage: 'Failed to query events data',
|
||||
}
|
||||
);
|
||||
|
||||
export const SHOW_EXTERNAL_ALERTS = i18n.translate(
|
||||
'xpack.securitySolution.eventsTab.showExternalAlerts',
|
||||
{
|
||||
defaultMessage: 'Show only external alerts',
|
||||
}
|
||||
);
|
||||
|
||||
export const EVENTS_GRAPH_TITLE = i18n.translate('xpack.securitySolution.eventsGraphTitle', {
|
||||
defaultMessage: 'Events',
|
||||
});
|
|
@ -6,13 +6,10 @@
|
|||
*/
|
||||
|
||||
import type { ColumnHeaderOptions } from '../../../../common/types/timeline';
|
||||
import { RowRendererId } from '../../../../common/types/timeline';
|
||||
import { defaultColumnHeaderType } from '../../../timelines/components/timeline/body/column_headers/default_headers';
|
||||
import { DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants';
|
||||
import type { SubsetTimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
|
||||
|
||||
export const alertsHeaders: ColumnHeaderOptions[] = [
|
||||
export const defaultAlertsHeaders: ColumnHeaderOptions[] = [
|
||||
{
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
id: '@timestamp',
|
||||
|
@ -56,9 +53,3 @@ export const alertsHeaders: ColumnHeaderOptions[] = [
|
|||
id: 'agent.type',
|
||||
},
|
||||
];
|
||||
|
||||
export const alertsDefaultModel: SubsetTimelineModel = {
|
||||
...timelineDefaults,
|
||||
columns: alertsHeaders,
|
||||
excludedRowRendererIds: Object.values(RowRendererId),
|
||||
};
|
|
@ -9,7 +9,7 @@ import type { ColumnHeaderOptions } from '../../../../common/types';
|
|||
import { defaultColumnHeaderType } from '../../../timelines/components/timeline/body/column_headers/default_headers';
|
||||
import { DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants';
|
||||
|
||||
export const defaultHeaders: ColumnHeaderOptions[] = [
|
||||
export const defaultEventHeaders: ColumnHeaderOptions[] = [
|
||||
{
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
id: '@timestamp',
|
||||
|
@ -29,6 +29,10 @@ export const defaultHeaders: ColumnHeaderOptions[] = [
|
|||
columnHeaderType: defaultColumnHeaderType,
|
||||
id: 'event.module',
|
||||
},
|
||||
{
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
id: 'agent.type',
|
||||
},
|
||||
{
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
id: 'event.dataset',
|
|
@ -5,11 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { defaultHeaders } from './default_headers';
|
||||
import { defaultEventHeaders } from './default_event_headers';
|
||||
import type { SubsetTimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
|
||||
|
||||
export const eventsDefaultModel: SubsetTimelineModel = {
|
||||
...timelineDefaults,
|
||||
columns: defaultHeaders,
|
||||
columns: defaultEventHeaders,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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 type { Filter } from '@kbn/es-query';
|
||||
|
||||
export const defaultAlertsFilters: Filter = {
|
||||
meta: {
|
||||
alias: null,
|
||||
negate: false,
|
||||
disabled: false,
|
||||
type: 'phrase',
|
||||
key: 'event.kind',
|
||||
params: {
|
||||
query: 'alert',
|
||||
},
|
||||
},
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
match: {
|
||||
'event.kind': 'alert',
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
|
@ -46,7 +46,7 @@ jest.mock('../../utils/route/use_route_spy', () => ({
|
|||
{
|
||||
detailName: 'mockHost',
|
||||
pageName: 'hosts',
|
||||
tabName: 'externalAlerts',
|
||||
tabName: 'events',
|
||||
},
|
||||
]),
|
||||
}));
|
||||
|
@ -181,7 +181,7 @@ describe('Matrix Histogram Component', () => {
|
|||
{
|
||||
detailName: 'mockHost',
|
||||
pageName: 'hosts',
|
||||
tabName: 'externalAlerts',
|
||||
tabName: 'events',
|
||||
},
|
||||
]);
|
||||
|
||||
|
@ -240,7 +240,7 @@ describe('Matrix Histogram Component', () => {
|
|||
{
|
||||
detailName: 'mockHost',
|
||||
pageName: 'hosts',
|
||||
tabName: 'externalAlerts',
|
||||
tabName: 'events',
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
|
@ -138,6 +138,11 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramComponentProps> =
|
|||
const [isInitialLoading, setIsInitialLoading] = useState(true);
|
||||
const [selectedStackByOption, setSelectedStackByOption] =
|
||||
useState<MatrixHistogramOption>(defaultStackByOption);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedStackByOption(defaultStackByOption);
|
||||
}, [defaultStackByOption]);
|
||||
|
||||
const setSelectedChartOptionCallback = useCallback(
|
||||
(event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setSelectedStackByOption(
|
||||
|
|
|
@ -27,9 +27,8 @@ const detectionAlertsTimelines = [TimelineId.detectionsPage, TimelineId.detectio
|
|||
/** the following `TimelineId`s are NOT detection alert tables */
|
||||
const otherTimelines = [
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.hostsPageExternalAlerts,
|
||||
TimelineId.hostsPageSessions,
|
||||
TimelineId.networkPageExternalAlerts,
|
||||
TimelineId.networkPageEvents,
|
||||
TimelineId.active,
|
||||
TimelineId.casePage,
|
||||
TimelineId.test,
|
||||
|
|
|
@ -155,7 +155,7 @@ let testProps = {
|
|||
browserFields: mockBrowserFields,
|
||||
field,
|
||||
indexPattern: mockIndexPattern,
|
||||
timelineId: TimelineId.hostsPageExternalAlerts,
|
||||
timelineId: TimelineId.hostsPageEvents,
|
||||
toggleTopN: jest.fn(),
|
||||
onFilterAdded: jest.fn(),
|
||||
value,
|
||||
|
|
|
@ -143,8 +143,7 @@ describe('TopN', () => {
|
|||
|
||||
const nonDetectionAlertTables = [
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.hostsPageExternalAlerts,
|
||||
TimelineId.networkPageExternalAlerts,
|
||||
TimelineId.networkPageEvents,
|
||||
TimelineId.casePage,
|
||||
];
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ jest.mock('../../utils/route/use_route_spy', () => ({
|
|||
{
|
||||
detailName: 'mockHost',
|
||||
pageName: 'hosts',
|
||||
tabName: 'externalAlerts',
|
||||
tabName: 'events',
|
||||
},
|
||||
]),
|
||||
}));
|
||||
|
|
|
@ -41,10 +41,7 @@ export const useLensAttributes = ({
|
|||
const [{ detailName, pageName, tabName }] = useRouteSpy();
|
||||
|
||||
const tabsFilters = useMemo(() => {
|
||||
if (
|
||||
pageName === SecurityPageName.hosts &&
|
||||
(tabName === HostsTableType.alerts || tabName === HostsTableType.events)
|
||||
) {
|
||||
if (pageName === SecurityPageName.hosts && tabName === HostsTableType.events) {
|
||||
return hostNameExistsFilter;
|
||||
}
|
||||
|
||||
|
|
|
@ -119,13 +119,6 @@ describe('Security app links', () => {
|
|||
...networkLinkItem,
|
||||
// all its links should be filtered for all different criteria
|
||||
links: [
|
||||
{
|
||||
id: SecurityPageName.networkExternalAlerts,
|
||||
title: 'external alerts',
|
||||
path: '/external_alerts',
|
||||
experimentalKey:
|
||||
'flagDisabled' as unknown as keyof typeof mockExperimentalDefaults,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.networkDns,
|
||||
title: 'dns',
|
||||
|
|
|
@ -81,7 +81,6 @@ export const mockGlobalState: State = {
|
|||
events: { activePage: 0, limit: 10 },
|
||||
uncommonProcesses: { activePage: 0, limit: 10 },
|
||||
anomalies: null,
|
||||
externalAlerts: { activePage: 0, limit: 10 },
|
||||
hostRisk: {
|
||||
activePage: 0,
|
||||
limit: 10,
|
||||
|
@ -103,7 +102,6 @@ export const mockGlobalState: State = {
|
|||
events: { activePage: 0, limit: 10 },
|
||||
uncommonProcesses: { activePage: 0, limit: 10 },
|
||||
anomalies: null,
|
||||
externalAlerts: { activePage: 0, limit: 10 },
|
||||
hostRisk: {
|
||||
activePage: 0,
|
||||
limit: 10,
|
||||
|
@ -223,14 +221,12 @@ export const mockGlobalState: State = {
|
|||
severitySelection: [],
|
||||
},
|
||||
[usersModel.UsersTableType.events]: { activePage: 0, limit: 10 },
|
||||
[usersModel.UsersTableType.alerts]: { activePage: 0, limit: 10 },
|
||||
},
|
||||
},
|
||||
details: {
|
||||
queries: {
|
||||
[usersModel.UsersTableType.anomalies]: null,
|
||||
[usersModel.UsersTableType.events]: { activePage: 0, limit: 10 },
|
||||
[usersModel.UsersTableType.alerts]: { activePage: 0, limit: 10 },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -16,7 +16,6 @@ import { initialHostsState, hostsReducer } from './store';
|
|||
|
||||
const HOST_TIMELINE_IDS: TimelineIdLiteral[] = [
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.hostsPageExternalAlerts,
|
||||
TimelineId.hostsPageSessions,
|
||||
];
|
||||
|
||||
|
|
|
@ -46,13 +46,6 @@ export const links: LinkItem = {
|
|||
}),
|
||||
path: `${HOSTS_PATH}/events`,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.hostsExternalAlerts,
|
||||
title: i18n.translate('xpack.securitySolution.appLinks.hosts.externalAlerts', {
|
||||
defaultMessage: 'External Alerts',
|
||||
}),
|
||||
path: `${HOSTS_PATH}/externalAlerts`,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.hostsRisk,
|
||||
title: i18n.translate('xpack.securitySolution.appLinks.hosts.risk', {
|
||||
|
|
|
@ -74,7 +74,6 @@ describe('body', () => {
|
|||
[HostsTableType.uncommonProcesses]: 'UncommonProcessQueryTabBody',
|
||||
[HostsTableType.anomalies]: 'AnomaliesQueryTabBody',
|
||||
[HostsTableType.events]: 'EventsQueryTabBody',
|
||||
[HostsTableType.alerts]: 'HostAlertsQueryTabBody',
|
||||
};
|
||||
|
||||
const mockHostDetailsPageFilters = getHostDetailsPageFilters('host-1');
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { Switch } from 'react-router-dom';
|
||||
import { Route } from '@kbn/kibana-react-plugin/public';
|
||||
|
||||
|
@ -16,7 +16,8 @@ import { HostsTableType } from '../../store/model';
|
|||
import { AnomaliesQueryTabBody } from '../../../common/containers/anomalies/anomalies_query_tab_body';
|
||||
import { useGlobalTime } from '../../../common/containers/use_global_time';
|
||||
import { AnomaliesHostTable } from '../../../common/components/ml/tables/anomalies_host_table';
|
||||
import { EventsQueryTabBody } from '../../../common/components/events_tab/events_query_tab_body';
|
||||
import { EventsQueryTabBody } from '../../../common/components/events_tab';
|
||||
import { hostNameExistsFilter } from '../../../common/components/visualization_actions/utils';
|
||||
|
||||
import type { HostDetailsTabsProps } from './types';
|
||||
import { type } from './utils';
|
||||
|
@ -25,7 +26,6 @@ import {
|
|||
HostsQueryTabBody,
|
||||
AuthenticationsQueryTabBody,
|
||||
UncommonProcessQueryTabBody,
|
||||
HostAlertsQueryTabBody,
|
||||
HostRiskTabBody,
|
||||
SessionsTabBody,
|
||||
} from '../navigation';
|
||||
|
@ -37,7 +37,7 @@ export const HostDetailsTabs = React.memo<HostDetailsTabsProps>(
|
|||
filterQuery,
|
||||
indexNames,
|
||||
indexPattern,
|
||||
pageFilters,
|
||||
pageFilters = [],
|
||||
setAbsoluteRangeDatePicker,
|
||||
hostDetailsPagePath,
|
||||
}) => {
|
||||
|
@ -84,6 +84,11 @@ export const HostDetailsTabs = React.memo<HostDetailsTabsProps>(
|
|||
updateDateRange,
|
||||
};
|
||||
|
||||
const externalAlertPageFilters = useMemo(
|
||||
() => [...hostNameExistsFilter, ...pageFilters],
|
||||
[pageFilters]
|
||||
);
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route path={`${hostDetailsPagePath}/:tabName(${HostsTableType.authentications})`}>
|
||||
|
@ -104,11 +109,9 @@ export const HostDetailsTabs = React.memo<HostDetailsTabsProps>(
|
|||
{...tabProps}
|
||||
pageFilters={pageFilters}
|
||||
timelineId={TimelineId.hostsPageEvents}
|
||||
externalAlertPageFilters={externalAlertPageFilters}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={`${hostDetailsPagePath}/:tabName(${HostsTableType.alerts})`}>
|
||||
<HostAlertsQueryTabBody {...tabProps} pageFilters={pageFilters} />
|
||||
</Route>
|
||||
<Route path={`${hostDetailsPagePath}/:tabName(${HostsTableType.risk})`}>
|
||||
<HostRiskTabBody {...tabProps} />
|
||||
</Route>
|
||||
|
|
|
@ -50,12 +50,6 @@ export const navTabsHostDetails = ({
|
|||
href: getTabsOnHostDetailsUrl(hostName, HostsTableType.events),
|
||||
disabled: false,
|
||||
},
|
||||
[HostsTableType.alerts]: {
|
||||
id: HostsTableType.alerts,
|
||||
name: i18n.NAVIGATION_ALERTS_TITLE,
|
||||
href: getTabsOnHostDetailsUrl(hostName, HostsTableType.alerts),
|
||||
disabled: false,
|
||||
},
|
||||
[HostsTableType.risk]: {
|
||||
id: HostsTableType.risk,
|
||||
name: i18n.NAVIGATION_HOST_RISK_TITLE,
|
||||
|
|
|
@ -25,7 +25,6 @@ const TabNameMappedToI18nKey: Record<HostsTableType, string> = {
|
|||
[HostsTableType.uncommonProcesses]: i18n.NAVIGATION_UNCOMMON_PROCESSES_TITLE,
|
||||
[HostsTableType.anomalies]: i18n.NAVIGATION_ANOMALIES_TITLE,
|
||||
[HostsTableType.events]: i18n.NAVIGATION_EVENTS_TITLE,
|
||||
[HostsTableType.alerts]: i18n.NAVIGATION_ALERTS_TITLE,
|
||||
[HostsTableType.risk]: i18n.NAVIGATION_HOST_RISK_TITLE,
|
||||
[HostsTableType.sessions]: i18n.NAVIGATION_SESSIONS_TITLE,
|
||||
};
|
||||
|
|
|
@ -74,12 +74,7 @@ const HostsComponent = () => {
|
|||
const containerElement = useRef<HTMLDivElement | null>(null);
|
||||
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
|
||||
const graphEventId = useShallowEqualSelector(
|
||||
(state) =>
|
||||
(
|
||||
getTimeline(state, TimelineId.hostsPageEvents) ??
|
||||
getTimeline(state, TimelineId.hostsPageExternalAlerts) ??
|
||||
timelineDefaults
|
||||
).graphEventId
|
||||
(state) => (getTimeline(state, TimelineId.hostsPageEvents) ?? timelineDefaults).graphEventId
|
||||
);
|
||||
const getGlobalFiltersQuerySelector = useMemo(
|
||||
() => inputsSelectors.globalFiltersQuerySelector(),
|
||||
|
@ -102,7 +97,7 @@ const HostsComponent = () => {
|
|||
const { uiSettings } = useKibana().services;
|
||||
const { tabName } = useParams<{ tabName: string }>();
|
||||
const tabsFilters: Filter[] = React.useMemo(() => {
|
||||
if (tabName === HostsTableType.alerts || tabName === HostsTableType.events) {
|
||||
if (tabName === HostsTableType.events) {
|
||||
return filters.length > 0 ? [...filters, ...hostNameExistsFilter] : hostNameExistsFilter;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import React, { memo, useCallback, useMemo } from 'react';
|
||||
import { Switch } from 'react-router-dom';
|
||||
import { Route } from '@kbn/kibana-react-plugin/public';
|
||||
|
||||
|
@ -16,7 +16,7 @@ import { HostsTableType } from '../store/model';
|
|||
import { AnomaliesQueryTabBody } from '../../common/containers/anomalies/anomalies_query_tab_body';
|
||||
import { AnomaliesHostTable } from '../../common/components/ml/tables/anomalies_host_table';
|
||||
import type { UpdateDateRange } from '../../common/components/charts/common';
|
||||
import { EventsQueryTabBody } from '../../common/components/events_tab/events_query_tab_body';
|
||||
import { EventsQueryTabBody } from '../../common/components/events_tab';
|
||||
import { HOSTS_PATH } from '../../../common/constants';
|
||||
|
||||
import {
|
||||
|
@ -25,14 +25,14 @@ import {
|
|||
UncommonProcessQueryTabBody,
|
||||
SessionsTabBody,
|
||||
} from './navigation';
|
||||
import { HostAlertsQueryTabBody } from './navigation/alerts_query_tab_body';
|
||||
import { TimelineId } from '../../../common/types';
|
||||
import { hostNameExistsFilter } from '../../common/components/visualization_actions/utils';
|
||||
|
||||
export const HostsTabs = memo<HostsTabsProps>(
|
||||
({
|
||||
deleteQuery,
|
||||
filterQuery,
|
||||
pageFilters,
|
||||
pageFilters = [],
|
||||
from,
|
||||
indexNames,
|
||||
isInitializing,
|
||||
|
@ -81,6 +81,10 @@ export const HostsTabs = memo<HostsTabsProps>(
|
|||
updateDateRange,
|
||||
};
|
||||
|
||||
const externalAlertPageFilters = useMemo(
|
||||
() => [...hostNameExistsFilter, ...pageFilters],
|
||||
[pageFilters]
|
||||
);
|
||||
return (
|
||||
<Switch>
|
||||
<Route path={`${HOSTS_PATH}/:tabName(${HostsTableType.hosts})`}>
|
||||
|
@ -98,13 +102,11 @@ export const HostsTabs = memo<HostsTabsProps>(
|
|||
<Route path={`${HOSTS_PATH}/:tabName(${HostsTableType.events})`}>
|
||||
<EventsQueryTabBody
|
||||
{...tabProps}
|
||||
timelineId={TimelineId.hostsPageEvents}
|
||||
pageFilters={pageFilters}
|
||||
timelineId={TimelineId.hostsPageEvents}
|
||||
externalAlertPageFilters={externalAlertPageFilters}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={`${HOSTS_PATH}/:tabName(${HostsTableType.alerts})`}>
|
||||
<HostAlertsQueryTabBody {...tabProps} pageFilters={pageFilters} />
|
||||
</Route>
|
||||
<Route path={`${HOSTS_PATH}/:tabName(${HostsTableType.sessions})`}>
|
||||
<SessionsTabBody {...tabProps} />
|
||||
</Route>
|
||||
|
|
|
@ -23,7 +23,6 @@ const getHostsTabPath = () =>
|
|||
`${HostsTableType.anomalies}|` +
|
||||
`${HostsTableType.events}|` +
|
||||
`${HostsTableType.risk}|` +
|
||||
`${HostsTableType.alerts}|` +
|
||||
`${HostsTableType.sessions})`;
|
||||
|
||||
const getHostDetailsTabPath = () =>
|
||||
|
@ -33,7 +32,6 @@ const getHostDetailsTabPath = () =>
|
|||
`${HostsTableType.anomalies}|` +
|
||||
`${HostsTableType.events}|` +
|
||||
`${HostsTableType.risk}|` +
|
||||
`${HostsTableType.alerts}|` +
|
||||
`${HostsTableType.sessions})`;
|
||||
|
||||
export const HostsContainer = React.memo(() => (
|
||||
|
|
|
@ -46,12 +46,6 @@ export const navTabsHosts = ({
|
|||
href: getTabsOnHostsUrl(HostsTableType.events),
|
||||
disabled: false,
|
||||
},
|
||||
[HostsTableType.alerts]: {
|
||||
id: HostsTableType.alerts,
|
||||
name: i18n.NAVIGATION_ALERTS_TITLE,
|
||||
href: getTabsOnHostsUrl(HostsTableType.alerts),
|
||||
disabled: false,
|
||||
},
|
||||
[HostsTableType.risk]: {
|
||||
id: HostsTableType.risk,
|
||||
name: i18n.NAVIGATION_HOST_RISK_TITLE,
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* 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, { useMemo } from 'react';
|
||||
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { AlertsView } from '../../../common/components/alerts_viewer';
|
||||
import { hostNameExistsFilter } from '../../../common/components/visualization_actions/utils';
|
||||
import type { AlertsComponentQueryProps } from './types';
|
||||
|
||||
export const HostAlertsQueryTabBody = React.memo((alertsProps: AlertsComponentQueryProps) => {
|
||||
const { pageFilters, ...rest } = alertsProps;
|
||||
const hostPageFilters = useMemo(
|
||||
() => (pageFilters != null ? [...hostNameExistsFilter, ...pageFilters] : hostNameExistsFilter),
|
||||
[pageFilters]
|
||||
);
|
||||
|
||||
return (
|
||||
<AlertsView
|
||||
entityType="events"
|
||||
timelineId={TimelineId.hostsPageExternalAlerts}
|
||||
{...rest}
|
||||
pageFilters={hostPageFilters}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
HostAlertsQueryTabBody.displayName = 'HostAlertsQueryTabBody';
|
|
@ -8,7 +8,6 @@
|
|||
export * from './authentications_query_tab_body';
|
||||
export * from './hosts_query_tab_body';
|
||||
export * from './uncommon_process_query_tab_body';
|
||||
export * from './alerts_query_tab_body';
|
||||
export * from './host_risk_tab_body';
|
||||
export * from './host_risk_score_tab_body';
|
||||
export * from './sessions_tab_body';
|
||||
|
|
|
@ -46,13 +46,6 @@ export const NAVIGATION_EVENTS_TITLE = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const NAVIGATION_ALERTS_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.hosts.navigation.alertsTitle',
|
||||
{
|
||||
defaultMessage: 'External alerts',
|
||||
}
|
||||
);
|
||||
|
||||
export const NAVIGATION_HOST_RISK_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.hosts.navigation.hostRiskTitle',
|
||||
{
|
||||
|
@ -74,12 +67,6 @@ export const ERROR_FETCHING_EVENTS_DATA = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const EVENTS_UNIT = (totalCount: number) =>
|
||||
i18n.translate('xpack.securitySolution.hosts.navigaton.eventsUnit', {
|
||||
values: { totalCount },
|
||||
defaultMessage: `{totalCount, plural, =1 {event} other {events}}`,
|
||||
});
|
||||
|
||||
export const VIEW_DASHBOARD_BUTTON = i18n.translate(
|
||||
'xpack.securitySolution.hosts.navigaton.hostRisk.viewDashboardButtonLabel',
|
||||
{
|
||||
|
|
|
@ -33,10 +33,6 @@ export const mockHostsState: HostsModel = {
|
|||
limit: DEFAULT_TABLE_LIMIT,
|
||||
},
|
||||
[HostsTableType.anomalies]: null,
|
||||
[HostsTableType.alerts]: {
|
||||
activePage: 4,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
},
|
||||
[HostsTableType.risk]: {
|
||||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
|
@ -73,10 +69,6 @@ export const mockHostsState: HostsModel = {
|
|||
limit: DEFAULT_TABLE_LIMIT,
|
||||
},
|
||||
[HostsTableType.anomalies]: null,
|
||||
[HostsTableType.alerts]: {
|
||||
activePage: 4,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
},
|
||||
[HostsTableType.risk]: {
|
||||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
|
@ -117,10 +109,6 @@ describe('Hosts redux store', () => {
|
|||
activePage: 0,
|
||||
limit: 10,
|
||||
},
|
||||
[HostsTableType.alerts]: {
|
||||
activePage: 0,
|
||||
limit: 10,
|
||||
},
|
||||
[HostsTableType.risk]: {
|
||||
activePage: 0,
|
||||
limit: 10,
|
||||
|
@ -158,10 +146,6 @@ describe('Hosts redux store', () => {
|
|||
activePage: 0,
|
||||
limit: 10,
|
||||
},
|
||||
[HostsTableType.alerts]: {
|
||||
activePage: 0,
|
||||
limit: 10,
|
||||
},
|
||||
[HostsTableType.risk]: {
|
||||
activePage: 0,
|
||||
limit: 10,
|
||||
|
|
|
@ -29,10 +29,6 @@ export const setHostPageQueriesActivePageToZero = (state: HostsModel): Queries =
|
|||
...state.page.queries[HostsTableType.uncommonProcesses],
|
||||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
},
|
||||
[HostsTableType.alerts]: {
|
||||
...state.page.queries[HostsTableType.alerts],
|
||||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
},
|
||||
});
|
||||
|
||||
export const setHostDetailsQueriesActivePageToZero = (state: HostsModel): Queries => ({
|
||||
|
@ -53,10 +49,6 @@ export const setHostDetailsQueriesActivePageToZero = (state: HostsModel): Querie
|
|||
...state.details.queries[HostsTableType.uncommonProcesses],
|
||||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
},
|
||||
[HostsTableType.alerts]: {
|
||||
...state.page.queries[HostsTableType.alerts],
|
||||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
},
|
||||
});
|
||||
|
||||
export const setHostsQueriesActivePageToZero = (state: HostsModel, type: HostsType): Queries => {
|
||||
|
|
|
@ -23,7 +23,6 @@ export enum HostsTableType {
|
|||
events = 'events',
|
||||
uncommonProcesses = 'uncommonProcesses',
|
||||
anomalies = 'anomalies',
|
||||
alerts = 'externalAlerts',
|
||||
risk = 'hostRisk',
|
||||
sessions = 'sessions',
|
||||
}
|
||||
|
@ -49,7 +48,6 @@ export interface Queries {
|
|||
[HostsTableType.events]: BasicQueryPaginated;
|
||||
[HostsTableType.uncommonProcesses]: BasicQueryPaginated;
|
||||
[HostsTableType.anomalies]: null | undefined;
|
||||
[HostsTableType.alerts]: BasicQueryPaginated;
|
||||
[HostsTableType.risk]: HostRiskScoreQuery;
|
||||
[HostsTableType.sessions]: BasicQueryPaginated;
|
||||
}
|
||||
|
|
|
@ -50,10 +50,6 @@ export const initialHostsState: HostsState = {
|
|||
limit: DEFAULT_TABLE_LIMIT,
|
||||
},
|
||||
[HostsTableType.anomalies]: null,
|
||||
[HostsTableType.alerts]: {
|
||||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
},
|
||||
[HostsTableType.risk]: {
|
||||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
|
@ -90,10 +86,6 @@ export const initialHostsState: HostsState = {
|
|||
limit: DEFAULT_TABLE_LIMIT,
|
||||
},
|
||||
[HostsTableType.anomalies]: null,
|
||||
[HostsTableType.alerts]: {
|
||||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
},
|
||||
[HostsTableType.risk]: {
|
||||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
|
|
|
@ -20,7 +20,7 @@ export class Network {
|
|||
return {
|
||||
routes,
|
||||
storageTimelines: {
|
||||
timelineById: getTimelinesInStorageByIds(storage, [TimelineId.networkPageExternalAlerts]),
|
||||
timelineById: getTimelinesInStorageByIds(storage, [TimelineId.networkPageEvents]),
|
||||
},
|
||||
store: {
|
||||
initialState: { network: initialNetworkState },
|
||||
|
|
|
@ -47,13 +47,6 @@ export const links: LinkItem = {
|
|||
}),
|
||||
path: `${NETWORK_PATH}/tls`,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.networkExternalAlerts,
|
||||
title: i18n.translate('xpack.securitySolution.appLinks.network.externalAlerts', {
|
||||
defaultMessage: 'External Alerts',
|
||||
}),
|
||||
path: `${NETWORK_PATH}/external-alerts`,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.networkAnomalies,
|
||||
title: i18n.translate('xpack.securitySolution.appLinks.hosts.anomalies', {
|
||||
|
|
|
@ -19,7 +19,7 @@ import type { GetSecuritySolutionUrl } from '../../../common/components/link_to'
|
|||
|
||||
export const type = networkModel.NetworkType.details;
|
||||
const TabNameMappedToI18nKey: Record<NetworkRouteType, string> = {
|
||||
[NetworkRouteType.alerts]: i18n.NAVIGATION_ALERTS_TITLE,
|
||||
[NetworkRouteType.alerts]: i18n.NAVIGATION_EVENTS_TITLE,
|
||||
[NetworkRouteType.anomalies]: i18n.NAVIGATION_ANOMALIES_TITLE,
|
||||
[NetworkRouteType.flows]: i18n.NAVIGATION_FLOWS_TITLE,
|
||||
[NetworkRouteType.dns]: i18n.NAVIGATION_DNS_TITLE,
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* 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 { TimelineId } from '../../../../common/types/timeline';
|
||||
import { AlertsView } from '../../../common/components/alerts_viewer';
|
||||
import type { NetworkComponentQueryProps } from './types';
|
||||
import { filterNetworkExternalAlertData } from '../../../common/components/visualization_actions/utils';
|
||||
|
||||
export const NetworkAlertsQueryTabBody = React.memo((alertsProps: NetworkComponentQueryProps) => (
|
||||
<AlertsView
|
||||
entityType="events"
|
||||
timelineId={TimelineId.networkPageExternalAlerts}
|
||||
{...alertsProps}
|
||||
pageFilters={filterNetworkExternalAlertData}
|
||||
/>
|
||||
));
|
||||
|
||||
NetworkAlertsQueryTabBody.displayName = 'NetworkAlertsQueryTabBody';
|
|
@ -9,7 +9,6 @@ export * from './network_routes';
|
|||
export * from './network_routes_loading';
|
||||
export * from './nav_tabs';
|
||||
export * from './utils';
|
||||
export * from './alerts_query_tab_body';
|
||||
export * from './countries_query_tab_body';
|
||||
export * from './dns_query_tab_body';
|
||||
export * from './http_query_tab_body';
|
||||
|
|
|
@ -47,7 +47,7 @@ export const navTabsNetwork = (hasMlUserPermissions: boolean): NetworkNavTab =>
|
|||
},
|
||||
[NetworkRouteType.alerts]: {
|
||||
id: NetworkRouteType.alerts,
|
||||
name: i18n.NAVIGATION_ALERTS_TITLE,
|
||||
name: i18n.NAVIGATION_EVENTS_TITLE,
|
||||
href: getTabsOnNetworkUrl(NetworkRouteType.alerts),
|
||||
disabled: false,
|
||||
},
|
||||
|
|
|
@ -18,11 +18,13 @@ import {
|
|||
DnsQueryTabBody,
|
||||
HttpQueryTabBody,
|
||||
IPsQueryTabBody,
|
||||
NetworkAlertsQueryTabBody,
|
||||
TlsQueryTabBody,
|
||||
} from '.';
|
||||
import { AnomaliesQueryTabBody } from '../../../common/containers/anomalies/anomalies_query_tab_body';
|
||||
import { EventsQueryTabBody } from '../../../common/components/events_tab';
|
||||
import { AnomaliesNetworkTable } from '../../../common/components/ml/tables/anomalies_network_table';
|
||||
import { filterNetworkExternalAlertData } from '../../../common/components/visualization_actions/utils';
|
||||
import { AnomaliesQueryTabBody } from '../../../common/containers/anomalies/anomalies_query_tab_body';
|
||||
import { TimelineId } from '../../../../common/types';
|
||||
import { ConditionalFlexGroup } from './conditional_flex_group';
|
||||
import type { NetworkRoutesProps } from './types';
|
||||
import { NetworkRouteType } from './types';
|
||||
|
@ -154,7 +156,11 @@ export const NetworkRoutes = React.memo<NetworkRoutesProps>(
|
|||
/>
|
||||
</Route>
|
||||
<Route path={`${NETWORK_PATH}/:tabName(${NetworkRouteType.alerts})`}>
|
||||
<NetworkAlertsQueryTabBody {...tabProps} />
|
||||
<EventsQueryTabBody
|
||||
pageFilters={filterNetworkExternalAlertData}
|
||||
timelineId={TimelineId.networkPageEvents}
|
||||
{...tabProps}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
|
|
|
@ -17,7 +17,7 @@ import type { GlobalTimeArgs } from '../../../common/containers/use_global_time'
|
|||
import type { SetAbsoluteRangeDatePicker } from '../types';
|
||||
import type { DocValueFields } from '../../../common/containers/source';
|
||||
|
||||
interface QueryTabBodyProps extends Pick<GlobalTimeArgs, 'setQuery' | 'deleteQuery'> {
|
||||
export interface QueryTabBodyProps extends Pick<GlobalTimeArgs, 'setQuery' | 'deleteQuery'> {
|
||||
endDate: string;
|
||||
filterQuery?: string | ESTermQuery;
|
||||
indexNames: string[];
|
||||
|
@ -62,7 +62,7 @@ export enum NetworkRouteType {
|
|||
anomalies = 'anomalies',
|
||||
tls = 'tls',
|
||||
http = 'http',
|
||||
alerts = 'external-alerts',
|
||||
alerts = 'events', // changed officially to events in #136427
|
||||
}
|
||||
|
||||
export type KeyNetworkNavTabWithoutMlPermission = NetworkRouteType.dns &
|
||||
|
|
|
@ -69,8 +69,7 @@ const NetworkComponent = React.memo<NetworkComponentProps>(
|
|||
const containerElement = useRef<HTMLDivElement | null>(null);
|
||||
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
|
||||
const graphEventId = useShallowEqualSelector(
|
||||
(state) =>
|
||||
(getTimeline(state, TimelineId.networkPageExternalAlerts) ?? timelineDefaults).graphEventId
|
||||
(state) => (getTimeline(state, TimelineId.networkPageEvents) ?? timelineDefaults).graphEventId
|
||||
);
|
||||
const getGlobalFiltersQuerySelector = useMemo(
|
||||
() => inputsSelectors.globalFiltersQuerySelector(),
|
||||
|
|
|
@ -46,9 +46,9 @@ export const NAVIGATION_ANOMALIES_TITLE = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const NAVIGATION_ALERTS_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.network.navigation.alertsTitle',
|
||||
export const NAVIGATION_EVENTS_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.network.navigation.eventsTitle',
|
||||
{
|
||||
defaultMessage: 'External alerts',
|
||||
defaultMessage: 'Events',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,353 +0,0 @@
|
|||
/*
|
||||
* 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 type { ReactWrapper } from 'enzyme';
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import '../../../common/mock/match_media';
|
||||
import '../../../common/mock/react_beautiful_dnd';
|
||||
import { useMatrixHistogramCombined } from '../../../common/containers/matrix_histogram';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import { mockIndexPattern, TestProviders } from '../../../common/mock';
|
||||
|
||||
import { AlertsByCategory } from '.';
|
||||
import { mockCasesContext } from '@kbn/cases-plugin/public/mocks/mock_cases_context';
|
||||
import { useRouteSpy } from '../../../common/utils/route/use_route_spy';
|
||||
|
||||
jest.mock('../../../common/components/link_to');
|
||||
jest.mock('../../../common/components/visualization_actions', () => ({
|
||||
VisualizationActions: jest.fn(() => <div data-test-subj="mock-viz-actions" />),
|
||||
}));
|
||||
|
||||
jest.mock('../../../common/containers/matrix_histogram', () => ({
|
||||
useMatrixHistogramCombined: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../common/lib/kibana', () => {
|
||||
const original = jest.requireActual('../../../common/lib/kibana');
|
||||
|
||||
return {
|
||||
...original,
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
...original.useKibana().services,
|
||||
cases: {
|
||||
ui: {
|
||||
getCasesContext: jest.fn().mockReturnValue(mockCasesContext),
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../common/utils/route/use_route_spy', () => ({
|
||||
useRouteSpy: jest.fn().mockReturnValue([
|
||||
{
|
||||
detailName: 'mockHost',
|
||||
pageName: 'hosts',
|
||||
tabName: 'externalAlerts',
|
||||
},
|
||||
]),
|
||||
}));
|
||||
|
||||
const from = '2020-03-31T06:00:00.000Z';
|
||||
const to = '2019-03-31T06:00:00.000Z';
|
||||
|
||||
describe('Alerts by category', () => {
|
||||
let wrapper: ReactWrapper;
|
||||
const testProps = {
|
||||
deleteQuery: jest.fn(),
|
||||
filters: [],
|
||||
from,
|
||||
indexNames: [],
|
||||
indexPattern: mockIndexPattern,
|
||||
setQuery: jest.fn(),
|
||||
to,
|
||||
query: {
|
||||
query: '',
|
||||
language: 'kuery',
|
||||
},
|
||||
};
|
||||
describe('before loading data', () => {
|
||||
beforeAll(async () => {
|
||||
(useMatrixHistogramCombined as jest.Mock).mockReturnValue([
|
||||
false,
|
||||
{
|
||||
data: null,
|
||||
inspect: false,
|
||||
totalCount: null,
|
||||
},
|
||||
]);
|
||||
|
||||
wrapper = mount(
|
||||
<TestProviders>
|
||||
<AlertsByCategory {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
});
|
||||
});
|
||||
|
||||
test('it renders the expected title', async () => {
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find('[data-test-subj="header-section-title"]').first().text()).toEqual(
|
||||
'External alert trend'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('it renders the subtitle (to prevent layout thrashing)', async () => {
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find('[data-test-subj="header-panel-subtitle"]').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('it renders the expected filter fields', async () => {
|
||||
await waitFor(() => {
|
||||
const expectedOptions = ['event.category', 'event.module'];
|
||||
|
||||
expectedOptions.forEach((option) => {
|
||||
expect(wrapper.find(`option[value="${option}"]`).text()).toEqual(option);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('it renders the `View alerts` button', async () => {
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find('[data-test-subj="view-alerts"]').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('it does NOT render the bar chart when data is not available', async () => {
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find(`.echChart`).exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('after loading data', () => {
|
||||
beforeAll(async () => {
|
||||
(useMatrixHistogramCombined as jest.Mock).mockReturnValue([
|
||||
false,
|
||||
{
|
||||
data: [
|
||||
{ x: 1, y: 2, g: 'g1' },
|
||||
{ x: 2, y: 4, g: 'g1' },
|
||||
{ x: 3, y: 6, g: 'g1' },
|
||||
{ x: 1, y: 1, g: 'g2' },
|
||||
{ x: 2, y: 3, g: 'g2' },
|
||||
{ x: 3, y: 5, g: 'g2' },
|
||||
],
|
||||
inspect: false,
|
||||
totalCount: 6,
|
||||
},
|
||||
]);
|
||||
|
||||
wrapper = mount(
|
||||
<TestProviders>
|
||||
<AlertsByCategory {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
wrapper.update();
|
||||
});
|
||||
|
||||
test('it renders the expected subtitle', async () => {
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find('[data-test-subj="header-panel-subtitle"]').text()).toEqual(
|
||||
'Showing: 6 external alerts'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('it renders the bar chart when data is available', async () => {
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find(`.echChart`).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('it shows visualization actions on host page', async () => {
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find('[data-test-subj="mock-viz-actions"]').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('it shows visualization actions on network page', async () => {
|
||||
(useRouteSpy as jest.Mock).mockReturnValue([
|
||||
{
|
||||
detailName: undefined,
|
||||
pageName: 'network',
|
||||
tabName: 'external-alerts',
|
||||
},
|
||||
]);
|
||||
|
||||
const testWrapper = mount(
|
||||
<TestProviders>
|
||||
<AlertsByCategory {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
testWrapper.update();
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(testWrapper.find('[data-test-subj="mock-viz-actions"]').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('it does not shows visualization actions on other pages', async () => {
|
||||
(useRouteSpy as jest.Mock).mockReturnValue([
|
||||
{
|
||||
detailName: undefined,
|
||||
pageName: 'overview',
|
||||
tabName: undefined,
|
||||
},
|
||||
]);
|
||||
const testWrapper = mount(
|
||||
<TestProviders>
|
||||
<AlertsByCategory {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
testWrapper.update();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(testWrapper.find('[data-test-subj="mock-viz-actions"]').exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Host page', () => {
|
||||
beforeAll(async () => {
|
||||
(useRouteSpy as jest.Mock).mockReturnValue([
|
||||
{
|
||||
detailName: 'mockHost',
|
||||
pageName: 'hosts',
|
||||
tabName: 'externalAlerts',
|
||||
},
|
||||
]);
|
||||
|
||||
(useMatrixHistogramCombined as jest.Mock).mockReturnValue([
|
||||
false,
|
||||
{
|
||||
data: [
|
||||
{ x: 1, y: 2, g: 'g1' },
|
||||
{ x: 2, y: 4, g: 'g1' },
|
||||
{ x: 3, y: 6, g: 'g1' },
|
||||
{ x: 1, y: 1, g: 'g2' },
|
||||
{ x: 2, y: 3, g: 'g2' },
|
||||
{ x: 3, y: 5, g: 'g2' },
|
||||
],
|
||||
inspect: false,
|
||||
totalCount: 6,
|
||||
},
|
||||
]);
|
||||
|
||||
wrapper = mount(
|
||||
<TestProviders>
|
||||
<AlertsByCategory {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
wrapper.update();
|
||||
});
|
||||
|
||||
test('it shows visualization actions', async () => {
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find('[data-test-subj="mock-viz-actions"]').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Network page', () => {
|
||||
beforeAll(async () => {
|
||||
(useRouteSpy as jest.Mock).mockReturnValue([
|
||||
{
|
||||
detailName: undefined,
|
||||
pageName: 'network',
|
||||
tabName: 'external-alerts',
|
||||
},
|
||||
]);
|
||||
(useMatrixHistogramCombined as jest.Mock).mockReturnValue([
|
||||
false,
|
||||
{
|
||||
data: [
|
||||
{ x: 1, y: 2, g: 'g1' },
|
||||
{ x: 2, y: 4, g: 'g1' },
|
||||
{ x: 3, y: 6, g: 'g1' },
|
||||
{ x: 1, y: 1, g: 'g2' },
|
||||
{ x: 2, y: 3, g: 'g2' },
|
||||
{ x: 3, y: 5, g: 'g2' },
|
||||
],
|
||||
inspect: false,
|
||||
totalCount: 6,
|
||||
},
|
||||
]);
|
||||
|
||||
wrapper = mount(
|
||||
<TestProviders>
|
||||
<AlertsByCategory {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
wrapper.update();
|
||||
});
|
||||
|
||||
test('it shows visualization actions', async () => {
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find('[data-test-subj="mock-viz-actions"]').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Othen than Host or Network page', () => {
|
||||
beforeAll(async () => {
|
||||
(useRouteSpy as jest.Mock).mockReturnValue([
|
||||
{
|
||||
detailName: undefined,
|
||||
pageName: 'overview',
|
||||
tabName: undefined,
|
||||
},
|
||||
]);
|
||||
(useMatrixHistogramCombined as jest.Mock).mockReturnValue([
|
||||
false,
|
||||
{
|
||||
data: [
|
||||
{ x: 1, y: 2, g: 'g1' },
|
||||
{ x: 2, y: 4, g: 'g1' },
|
||||
{ x: 3, y: 6, g: 'g1' },
|
||||
{ x: 1, y: 1, g: 'g2' },
|
||||
{ x: 2, y: 3, g: 'g2' },
|
||||
{ x: 3, y: 5, g: 'g2' },
|
||||
],
|
||||
inspect: false,
|
||||
totalCount: 6,
|
||||
},
|
||||
]);
|
||||
|
||||
wrapper = mount(
|
||||
<TestProviders>
|
||||
<AlertsByCategory {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
wrapper.update();
|
||||
});
|
||||
|
||||
test('it does not shows visualization actions', async () => {
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find('[data-test-subj="mock-viz-actions"]').exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,145 +0,0 @@
|
|||
/*
|
||||
* 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 numeral from '@elastic/numeral';
|
||||
import React, { useEffect, useMemo, useCallback } from 'react';
|
||||
import { Position } from '@elastic/charts';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import type { DataViewBase, Filter, Query } from '@kbn/es-query';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { getEsQueryConfig } from '@kbn/data-plugin/common';
|
||||
import { DEFAULT_NUMBER_FORMAT, APP_UI_ID } from '../../../../common/constants';
|
||||
import { SHOWING, UNIT } from '../../../common/components/alerts_viewer/translations';
|
||||
import { MatrixHistogram } from '../../../common/components/matrix_histogram';
|
||||
import { useKibana, useUiSetting$ } from '../../../common/lib/kibana';
|
||||
import { convertToBuildEsQuery } from '../../../common/lib/keury';
|
||||
import { HostsTableType } from '../../../hosts/store/model';
|
||||
|
||||
import * as i18n from '../../pages/translations';
|
||||
import {
|
||||
alertsStackByOptions,
|
||||
histogramConfigs,
|
||||
} from '../../../common/components/alerts_viewer/histogram_configs';
|
||||
import type { MatrixHistogramConfigs } from '../../../common/components/matrix_histogram/types';
|
||||
import { getTabsOnHostsUrl } from '../../../common/components/link_to/redirect_to_hosts';
|
||||
import type { GlobalTimeArgs } from '../../../common/containers/use_global_time';
|
||||
import { SecurityPageName } from '../../../app/types';
|
||||
import { useFormatUrl } from '../../../common/components/link_to';
|
||||
import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query';
|
||||
|
||||
const ID = 'alertsByCategoryOverview';
|
||||
|
||||
const DEFAULT_STACK_BY = 'event.module';
|
||||
|
||||
const StyledLinkButton = styled(EuiButton)`
|
||||
margin-left: 0;
|
||||
@media only screen and (min-width: ${(props) => props.theme.eui.euiBreakpoints.m}) {
|
||||
margin-left: ${({ theme }) => theme.eui.euiSizeL};
|
||||
}
|
||||
`;
|
||||
interface Props extends Pick<GlobalTimeArgs, 'from' | 'to' | 'deleteQuery' | 'setQuery'> {
|
||||
filters: Filter[];
|
||||
hideHeaderChildren?: boolean;
|
||||
indexPattern: DataViewBase;
|
||||
indexNames: string[];
|
||||
query: Query;
|
||||
}
|
||||
|
||||
const AlertsByCategoryComponent: React.FC<Props> = ({
|
||||
deleteQuery,
|
||||
filters,
|
||||
from,
|
||||
hideHeaderChildren = false,
|
||||
indexPattern,
|
||||
indexNames,
|
||||
query,
|
||||
setQuery,
|
||||
to,
|
||||
}) => {
|
||||
const {
|
||||
uiSettings,
|
||||
application: { navigateToApp },
|
||||
} = useKibana().services;
|
||||
const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.hosts);
|
||||
const [defaultNumberFormat] = useUiSetting$<string>(DEFAULT_NUMBER_FORMAT);
|
||||
|
||||
const goToHostAlerts = useCallback(
|
||||
(ev) => {
|
||||
ev.preventDefault();
|
||||
navigateToApp(APP_UI_ID, {
|
||||
deepLinkId: SecurityPageName.hosts,
|
||||
path: getTabsOnHostsUrl(HostsTableType.alerts, urlSearch),
|
||||
});
|
||||
},
|
||||
[navigateToApp, urlSearch]
|
||||
);
|
||||
|
||||
const alertsCountViewAlertsButton = useMemo(
|
||||
() => (
|
||||
<StyledLinkButton
|
||||
data-test-subj="view-alerts"
|
||||
onClick={goToHostAlerts}
|
||||
href={formatUrl(getTabsOnHostsUrl(HostsTableType.alerts))}
|
||||
>
|
||||
{i18n.VIEW_ALERTS}
|
||||
</StyledLinkButton>
|
||||
),
|
||||
[goToHostAlerts, formatUrl]
|
||||
);
|
||||
|
||||
const alertsByCategoryHistogramConfigs: MatrixHistogramConfigs = useMemo(
|
||||
() => ({
|
||||
...histogramConfigs,
|
||||
defaultStackByOption:
|
||||
alertsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ?? alertsStackByOptions[0],
|
||||
subtitle: (totalCount: number) =>
|
||||
`${SHOWING}: ${numeral(totalCount).format(defaultNumberFormat)} ${UNIT(totalCount)}`,
|
||||
legendPosition: Position.Right,
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
);
|
||||
|
||||
const [filterQuery, kqlError] = useMemo(
|
||||
() =>
|
||||
convertToBuildEsQuery({
|
||||
config: getEsQueryConfig(uiSettings),
|
||||
indexPattern,
|
||||
queries: [query],
|
||||
filters,
|
||||
}),
|
||||
[filters, indexPattern, uiSettings, query]
|
||||
);
|
||||
|
||||
useInvalidFilterQuery({ id: ID, filterQuery, kqlError, query, startDate: from, endDate: to });
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (deleteQuery) {
|
||||
deleteQuery({ id: ID });
|
||||
}
|
||||
};
|
||||
}, [deleteQuery]);
|
||||
|
||||
return (
|
||||
<MatrixHistogram
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
headerChildren={hideHeaderChildren ? null : alertsCountViewAlertsButton}
|
||||
id={ID}
|
||||
indexNames={indexNames}
|
||||
setQuery={setQuery}
|
||||
startDate={from}
|
||||
{...alertsByCategoryHistogramConfigs}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
AlertsByCategoryComponent.displayName = 'AlertsByCategoryComponent';
|
||||
|
||||
export const AlertsByCategory = React.memo(AlertsByCategoryComponent);
|
|
@ -26,8 +26,8 @@ import { convertToBuildEsQuery } from '../../../common/lib/keury';
|
|||
import { useKibana, useUiSetting$ } from '../../../common/lib/kibana';
|
||||
import {
|
||||
eventsStackByOptions,
|
||||
histogramConfigs,
|
||||
} from '../../../common/components/events_tab/events_query_tab_body';
|
||||
eventsHistogramConfig,
|
||||
} from '../../../common/components/events_tab/histogram_configurations';
|
||||
import { HostsTableType } from '../../../hosts/store/model';
|
||||
import type { InputsModelId } from '../../../common/store/inputs/constants';
|
||||
import type { GlobalTimeArgs } from '../../../common/containers/use_global_time';
|
||||
|
@ -154,9 +154,9 @@ const EventsByDatasetComponent: React.FC<Props> = ({
|
|||
|
||||
const eventsByDatasetHistogramConfigs: MatrixHistogramConfigs = useMemo(
|
||||
() => ({
|
||||
...histogramConfigs,
|
||||
...eventsHistogramConfig,
|
||||
stackByOptions:
|
||||
onlyField != null ? [getHistogramOption(onlyField)] : histogramConfigs.stackByOptions,
|
||||
onlyField != null ? [getHistogramOption(onlyField)] : eventsHistogramConfig.stackByOptions,
|
||||
defaultStackByOption:
|
||||
onlyField != null
|
||||
? getHistogramOption(onlyField)
|
||||
|
|
|
@ -5,5 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export const EXTERNAL_ALERTS_TAB = '[data-test-subj="navigation-externalAlerts"]';
|
||||
export const EXTERNAL_ALERTS_TAB_CONTENT = '[data-test-subj="events-viewer-panel"]';
|
||||
export { SignalsByCategory } from './signals_by_category';
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 { act, render } from '@testing-library/react';
|
||||
|
||||
import { TestProviders } from '../../../common/mock';
|
||||
import { SignalsByCategory } from './signals_by_category';
|
||||
|
||||
jest.mock('react-router-dom', () => {
|
||||
const originalModule = jest.requireActual('react-router-dom');
|
||||
return {
|
||||
...originalModule,
|
||||
useLocation: jest.fn().mockReturnValue({ pathname: '' }),
|
||||
};
|
||||
});
|
||||
|
||||
const mockUseFiltersForSignals = jest.fn(() => []);
|
||||
jest.mock('./use_filters_for_signals_by_category', () => ({
|
||||
useFiltersForSignalsByCategory: () => mockUseFiltersForSignals(),
|
||||
}));
|
||||
|
||||
const props = {
|
||||
query: {
|
||||
query: '',
|
||||
language: 'kuery',
|
||||
},
|
||||
filters: [],
|
||||
};
|
||||
|
||||
const renderComponent = () =>
|
||||
render(
|
||||
<TestProviders>
|
||||
<SignalsByCategory {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
describe('SignalsByCategory', () => {
|
||||
it('Renders to the page', () => {
|
||||
act(() => {
|
||||
const { getByText } = renderComponent();
|
||||
expect(getByText('Alert trend')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -85,8 +85,7 @@ const SignalsByCategoryComponent: React.FC<Props> = ({
|
|||
showTotalAlertsCount={true}
|
||||
signalIndexName={signalIndexName}
|
||||
runtimeMappings={runtimeMappings}
|
||||
timelineId={timelineId}
|
||||
title={i18n.ALERT_COUNT}
|
||||
title={i18n.ALERT_TREND}
|
||||
titleSize={onlyField == null ? 'm' : 's'}
|
||||
updateDateRange={updateDateRangeCallback}
|
||||
/>
|
|
@ -8,7 +8,6 @@
|
|||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiShowFor } from '@elastic/eui';
|
||||
import React, { useCallback, useState, useMemo } from 'react';
|
||||
|
||||
import { AlertsByCategory } from '../components/alerts_by_category';
|
||||
import { FiltersGlobal } from '../../common/components/filters_global';
|
||||
import { SiemSearchBar } from '../../common/components/search_bar';
|
||||
import { SecuritySolutionPageWrapper } from '../../common/components/page_wrapper';
|
||||
|
@ -95,25 +94,10 @@ const OverviewComponent = () => {
|
|||
<EuiFlexItem grow={3}>
|
||||
<EuiFlexGroup direction="column" responsive={false} gutterSize="none">
|
||||
{hasIndexRead && hasKibanaREAD && (
|
||||
<>
|
||||
<EuiFlexItem grow={false}>
|
||||
<SignalsByCategory filters={filters} query={query} />
|
||||
<EuiSpacer size="l" />
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<AlertsByCategory
|
||||
deleteQuery={deleteQuery}
|
||||
filters={filters}
|
||||
from={from}
|
||||
indexPattern={indexPattern}
|
||||
indexNames={selectedPatterns}
|
||||
query={query}
|
||||
setQuery={setQuery}
|
||||
to={to}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
)}
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
|
|
|
@ -25,8 +25,8 @@ export const RECENT_TIMELINES = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const ALERT_COUNT = i18n.translate('xpack.securitySolution.overview.signalCountTitle', {
|
||||
defaultMessage: 'Detection alert trend',
|
||||
export const ALERT_TREND = i18n.translate('xpack.securitySolution.overview.signalCountTitle', {
|
||||
defaultMessage: 'Alert trend',
|
||||
});
|
||||
|
||||
export const TOP = (fieldName: string) =>
|
||||
|
|
|
@ -94,16 +94,42 @@ tr:hover .c3:focus::before {
|
|||
background-image: linear-gradient( 135deg, #1d1e24 25%, transparent 25% ), linear-gradient( -135deg, #1d1e24 25%, transparent 25% ), linear-gradient( 135deg, transparent 75%, #1d1e24 75% ), linear-gradient( -135deg, transparent 75%, #1d1e24 75% );
|
||||
}
|
||||
|
||||
.c2 {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.c2 [data-rbd-placeholder-context-id] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.c4 > span.euiToolTipAnchor {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.c4 > span.euiToolTipAnchor.eui-textTruncate {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.c5 {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.c22 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.c21 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.c7 {
|
||||
margin-right: 3px;
|
||||
.c15 {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.c8 {
|
||||
margin: 0 5px;
|
||||
.c14 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.c17 {
|
||||
|
@ -134,13 +160,12 @@ tr:hover .c3:focus::before {
|
|||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.c15 {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
.c7 {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.c14 {
|
||||
margin-right: 5px;
|
||||
.c8 {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.c12 {
|
||||
|
@ -166,31 +191,6 @@ tr:hover .c3:focus::before {
|
|||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.c2 [data-rbd-placeholder-context-id] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.c4 > span.euiToolTipAnchor {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.c4 > span.euiToolTipAnchor.eui-textTruncate {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.c5 {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.c22 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentCenter euiFlexGroup--directionColumn euiFlexGroup--responsive euiFlexGroup--wrap"
|
||||
data-test-subj="netflow-rows"
|
||||
|
|
|
@ -107,6 +107,31 @@ tr:hover .c5:focus::before {
|
|||
background-image: linear-gradient( 135deg, #1d1e24 25%, transparent 25% ), linear-gradient( -135deg, #1d1e24 25%, transparent 25% ), linear-gradient( 135deg, transparent 75%, #1d1e24 75% ), linear-gradient( -135deg, transparent 75%, #1d1e24 75% );
|
||||
}
|
||||
|
||||
.c4 {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.c4 [data-rbd-placeholder-context-id] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.c6 > span.euiToolTipAnchor {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.c6 > span.euiToolTipAnchor.eui-textTruncate {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.c7 {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.c24 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.c23 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
@ -179,31 +204,6 @@ tr:hover .c5:focus::before {
|
|||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.c4 {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.c4 [data-rbd-placeholder-context-id] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.c6 > span.euiToolTipAnchor {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.c6 > span.euiToolTipAnchor.eui-textTruncate {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.c7 {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.c24 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
|
|
@ -17,13 +17,7 @@ describe('skipQueryForDetectionsPage', () => {
|
|||
skipQueryForDetectionsPage(TimelineId.hostsPageEvents, ['auditbeat-*', 'filebeat-*'])
|
||||
).toBe(false);
|
||||
expect(
|
||||
skipQueryForDetectionsPage(TimelineId.hostsPageExternalAlerts, ['auditbeat-*', 'filebeat-*'])
|
||||
).toBe(false);
|
||||
expect(
|
||||
skipQueryForDetectionsPage(TimelineId.networkPageExternalAlerts, [
|
||||
'auditbeat-*',
|
||||
'filebeat-*',
|
||||
])
|
||||
skipQueryForDetectionsPage(TimelineId.networkPageEvents, ['auditbeat-*', 'filebeat-*'])
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
|
|
|
@ -59,10 +59,10 @@ describe('SiemLocalStorage', () => {
|
|||
it('adds a timeline when storage contains another timelines', () => {
|
||||
const timelineStorage = useTimelinesStorage();
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.usersPageEvents, mockTimelineModel);
|
||||
expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({
|
||||
[TimelineId.hostsPageEvents]: timelineToStore,
|
||||
[TimelineId.hostsPageExternalAlerts]: timelineToStore,
|
||||
[TimelineId.usersPageEvents]: timelineToStore,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -71,11 +71,11 @@ describe('SiemLocalStorage', () => {
|
|||
it('gets all timelines correctly', () => {
|
||||
const timelineStorage = useTimelinesStorage();
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.usersPageEvents, mockTimelineModel);
|
||||
const timelines = timelineStorage.getAllTimelines();
|
||||
expect(timelines).toEqual({
|
||||
[TimelineId.hostsPageEvents]: timelineToStore,
|
||||
[TimelineId.hostsPageExternalAlerts]: timelineToStore,
|
||||
[TimelineId.usersPageEvents]: timelineToStore,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -99,14 +99,14 @@ describe('SiemLocalStorage', () => {
|
|||
it('gets timelines correctly', () => {
|
||||
const timelineStorage = useTimelinesStorage();
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.usersPageEvents, mockTimelineModel);
|
||||
const timelines = getTimelinesInStorageByIds(storage, [
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.hostsPageExternalAlerts,
|
||||
TimelineId.usersPageEvents,
|
||||
]);
|
||||
expect(timelines).toEqual({
|
||||
[TimelineId.hostsPageEvents]: timelineToStore,
|
||||
[TimelineId.hostsPageExternalAlerts]: timelineToStore,
|
||||
[TimelineId.usersPageEvents]: timelineToStore,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -125,7 +125,7 @@ describe('SiemLocalStorage', () => {
|
|||
it('returns empty timelime when a specific timeline does not exists', () => {
|
||||
const timelineStorage = useTimelinesStorage();
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel);
|
||||
const timelines = getTimelinesInStorageByIds(storage, [TimelineId.hostsPageExternalAlerts]);
|
||||
const timelines = getTimelinesInStorageByIds(storage, [TimelineId.usersPageEvents]);
|
||||
expect(timelines).toEqual({});
|
||||
});
|
||||
|
||||
|
@ -134,7 +134,7 @@ describe('SiemLocalStorage', () => {
|
|||
timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel);
|
||||
const timelines = getTimelinesInStorageByIds(storage, [
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.hostsPageExternalAlerts,
|
||||
TimelineId.usersPageEvents,
|
||||
]);
|
||||
expect(timelines).toEqual({
|
||||
[TimelineId.hostsPageEvents]: timelineToStore,
|
||||
|
@ -154,10 +154,10 @@ describe('SiemLocalStorage', () => {
|
|||
})),
|
||||
};
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageEvents, unmigratedMockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.usersPageEvents, mockTimelineModel);
|
||||
const timelines = getTimelinesInStorageByIds(storage, [
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.hostsPageExternalAlerts,
|
||||
TimelineId.usersPageEvents,
|
||||
]);
|
||||
|
||||
// all legacy `width` values are migrated to `initialWidth`:
|
||||
|
@ -171,7 +171,7 @@ describe('SiemLocalStorage', () => {
|
|||
width: 98765,
|
||||
})),
|
||||
},
|
||||
[TimelineId.hostsPageExternalAlerts]: {
|
||||
[TimelineId.usersPageEvents]: {
|
||||
...timelineToStore,
|
||||
columns: getExpectedColumns(mockTimelineModel),
|
||||
},
|
||||
|
@ -190,10 +190,10 @@ describe('SiemLocalStorage', () => {
|
|||
})),
|
||||
};
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageEvents, unmigratedMockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.usersPageEvents, mockTimelineModel);
|
||||
const timelines = getTimelinesInStorageByIds(storage, [
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.hostsPageExternalAlerts,
|
||||
TimelineId.usersPageEvents,
|
||||
]);
|
||||
|
||||
expect(timelines).toStrictEqual({
|
||||
|
@ -206,7 +206,7 @@ describe('SiemLocalStorage', () => {
|
|||
width: 98765,
|
||||
})),
|
||||
},
|
||||
[TimelineId.hostsPageExternalAlerts]: {
|
||||
[TimelineId.usersPageEvents]: {
|
||||
...timelineToStore,
|
||||
columns: getExpectedColumns(mockTimelineModel),
|
||||
},
|
||||
|
@ -225,10 +225,10 @@ describe('SiemLocalStorage', () => {
|
|||
})),
|
||||
};
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageEvents, unmigratedMockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.usersPageEvents, mockTimelineModel);
|
||||
const timelines = getTimelinesInStorageByIds(storage, [
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.hostsPageExternalAlerts,
|
||||
TimelineId.usersPageEvents,
|
||||
]);
|
||||
|
||||
// all legacy `label` values are migrated to `displayAsText`:
|
||||
|
@ -241,7 +241,7 @@ describe('SiemLocalStorage', () => {
|
|||
label: `A legacy label ${i}`,
|
||||
})),
|
||||
},
|
||||
[TimelineId.hostsPageExternalAlerts]: {
|
||||
[TimelineId.usersPageEvents]: {
|
||||
...timelineToStore,
|
||||
columns: getExpectedColumns(mockTimelineModel),
|
||||
},
|
||||
|
@ -262,10 +262,10 @@ describe('SiemLocalStorage', () => {
|
|||
})),
|
||||
};
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageEvents, unmigratedMockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.usersPageEvents, mockTimelineModel);
|
||||
const timelines = getTimelinesInStorageByIds(storage, [
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.hostsPageExternalAlerts,
|
||||
TimelineId.usersPageEvents,
|
||||
]);
|
||||
|
||||
expect(timelines).toStrictEqual({
|
||||
|
@ -278,7 +278,7 @@ describe('SiemLocalStorage', () => {
|
|||
label: `A legacy label ${i}`,
|
||||
})),
|
||||
},
|
||||
[TimelineId.hostsPageExternalAlerts]: {
|
||||
[TimelineId.usersPageEvents]: {
|
||||
...timelineToStore,
|
||||
columns: getExpectedColumns(mockTimelineModel),
|
||||
},
|
||||
|
@ -296,10 +296,10 @@ describe('SiemLocalStorage', () => {
|
|||
TimelineId.hostsPageEvents,
|
||||
invalidColumnsMockTimelineModel as unknown as TimelineModel
|
||||
);
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.usersPageEvents, mockTimelineModel);
|
||||
const timelines = getTimelinesInStorageByIds(storage, [
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.hostsPageExternalAlerts,
|
||||
TimelineId.usersPageEvents,
|
||||
]);
|
||||
|
||||
expect(timelines).toStrictEqual({
|
||||
|
@ -307,7 +307,7 @@ describe('SiemLocalStorage', () => {
|
|||
...timelineToStore,
|
||||
columns: 'this is NOT an array',
|
||||
},
|
||||
[TimelineId.hostsPageExternalAlerts]: {
|
||||
[TimelineId.usersPageEvents]: {
|
||||
...timelineToStore,
|
||||
columns: getExpectedColumns(mockTimelineModel),
|
||||
},
|
||||
|
@ -319,11 +319,11 @@ describe('SiemLocalStorage', () => {
|
|||
it('gets timelines correctly', () => {
|
||||
const timelineStorage = useTimelinesStorage();
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel);
|
||||
timelineStorage.addTimeline(TimelineId.usersPageEvents, mockTimelineModel);
|
||||
const timelines = getAllTimelinesInStorage(storage);
|
||||
expect(timelines).toEqual({
|
||||
[TimelineId.hostsPageEvents]: timelineToStore,
|
||||
[TimelineId.hostsPageExternalAlerts]: timelineToStore,
|
||||
[TimelineId.usersPageEvents]: timelineToStore,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -343,10 +343,10 @@ describe('SiemLocalStorage', () => {
|
|||
|
||||
it('adds a timeline when storage contains another timelines', () => {
|
||||
addTimelineInStorage(storage, TimelineId.hostsPageEvents, mockTimelineModel);
|
||||
addTimelineInStorage(storage, TimelineId.hostsPageExternalAlerts, mockTimelineModel);
|
||||
addTimelineInStorage(storage, TimelineId.usersPageEvents, mockTimelineModel);
|
||||
expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({
|
||||
[TimelineId.hostsPageEvents]: timelineToStore,
|
||||
[TimelineId.hostsPageExternalAlerts]: timelineToStore,
|
||||
[TimelineId.usersPageEvents]: timelineToStore,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -56,12 +56,5 @@ export const links: LinkItem = {
|
|||
}),
|
||||
path: `${USERS_PATH}/events`,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.usersExternalAlerts,
|
||||
title: i18n.translate('xpack.securitySolution.appLinks.users.externalAlerts', {
|
||||
defaultMessage: 'External Alerts',
|
||||
}),
|
||||
path: `${USERS_PATH}/externalAlerts`,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -10,6 +10,6 @@ import { UsersTableType } from '../store/model';
|
|||
|
||||
export const usersDetailsPagePath = `${USERS_PATH}/:detailName`;
|
||||
|
||||
export const usersTabPath = `${USERS_PATH}/:tabName(${UsersTableType.allUsers}|${UsersTableType.authentications}|${UsersTableType.anomalies}|${UsersTableType.risk}|${UsersTableType.events}|${UsersTableType.alerts})`;
|
||||
export const usersTabPath = `${USERS_PATH}/:tabName(${UsersTableType.allUsers}|${UsersTableType.authentications}|${UsersTableType.anomalies}|${UsersTableType.risk}|${UsersTableType.events}|)`;
|
||||
|
||||
export const usersDetailsTabPath = `${usersDetailsPagePath}/:tabName(${UsersTableType.authentications}|${UsersTableType.anomalies}|${UsersTableType.events}|${UsersTableType.alerts}|${UsersTableType.risk})`;
|
||||
export const usersDetailsTabPath = `${usersDetailsPagePath}/:tabName(${UsersTableType.authentications}|${UsersTableType.anomalies}|${UsersTableType.events}|${UsersTableType.risk})`;
|
||||
|
|
|
@ -18,8 +18,7 @@ import type { UpdateDateRange } from '../../../common/components/charts/common';
|
|||
import type { Anomaly } from '../../../common/components/ml/types';
|
||||
import { usersDetailsPagePath } from '../constants';
|
||||
import { TimelineId } from '../../../../common/types';
|
||||
import { EventsQueryTabBody } from '../../../common/components/events_tab/events_query_tab_body';
|
||||
import { AlertsView } from '../../../common/components/alerts_viewer';
|
||||
import { EventsQueryTabBody } from '../../../common/components/events_tab';
|
||||
import { userNameExistsFilter } from './helpers';
|
||||
import { AuthenticationsQueryTabBody } from '../navigation';
|
||||
import { UserRiskTabBody } from '../navigation/user_risk_tab_body';
|
||||
|
@ -36,7 +35,7 @@ export const UsersDetailsTabs = React.memo<UsersDetailsTabsProps>(
|
|||
type,
|
||||
setAbsoluteRangeDatePicker,
|
||||
detailName,
|
||||
pageFilters,
|
||||
pageFilters = [],
|
||||
}) => {
|
||||
const narrowDateRange = useCallback(
|
||||
(score: Anomaly, interval: string) => {
|
||||
|
@ -65,12 +64,6 @@ export const UsersDetailsTabs = React.memo<UsersDetailsTabsProps>(
|
|||
[setAbsoluteRangeDatePicker]
|
||||
);
|
||||
|
||||
const alertsPageFilters = useMemo(
|
||||
() =>
|
||||
pageFilters != null ? [...userNameExistsFilter, ...pageFilters] : userNameExistsFilter,
|
||||
[pageFilters]
|
||||
);
|
||||
|
||||
const tabProps = {
|
||||
deleteQuery,
|
||||
endDate: to,
|
||||
|
@ -85,6 +78,11 @@ export const UsersDetailsTabs = React.memo<UsersDetailsTabsProps>(
|
|||
userName: detailName,
|
||||
};
|
||||
|
||||
const externalAlertPageFilters = useMemo(
|
||||
() => [...userNameExistsFilter, ...pageFilters],
|
||||
[pageFilters]
|
||||
);
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route path={`${usersDetailsPagePath}/:tabName(${UsersTableType.authentications})`}>
|
||||
|
@ -98,15 +96,7 @@ export const UsersDetailsTabs = React.memo<UsersDetailsTabsProps>(
|
|||
{...tabProps}
|
||||
pageFilters={pageFilters}
|
||||
timelineId={TimelineId.usersPageEvents}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path={`${usersDetailsPagePath}/:tabName(${UsersTableType.alerts})`}>
|
||||
<AlertsView
|
||||
entityType="events"
|
||||
timelineId={TimelineId.usersPageExternalAlerts}
|
||||
pageFilters={alertsPageFilters}
|
||||
{...tabProps}
|
||||
externalAlertPageFilters={externalAlertPageFilters}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={`${usersDetailsPagePath}/:tabName(${UsersTableType.risk})`}>
|
||||
|
|
|
@ -40,12 +40,6 @@ export const navTabsUsersDetails = (
|
|||
href: getTabsOnUsersDetailsUrl(userName, UsersTableType.events),
|
||||
disabled: false,
|
||||
},
|
||||
[UsersTableType.alerts]: {
|
||||
id: UsersTableType.alerts,
|
||||
name: i18n.NAVIGATION_ALERTS_TITLE,
|
||||
href: getTabsOnUsersDetailsUrl(userName, UsersTableType.alerts),
|
||||
disabled: false,
|
||||
},
|
||||
[UsersTableType.risk]: {
|
||||
id: UsersTableType.risk,
|
||||
name: i18n.NAVIGATION_RISK_TITLE,
|
||||
|
|
|
@ -6,14 +6,21 @@
|
|||
*/
|
||||
|
||||
import type { ActionCreator } from 'typescript-fsa';
|
||||
import type { DataViewBase, Filter } from '@kbn/es-query';
|
||||
|
||||
import type { DataViewBase, Filter, Query } from '@kbn/es-query';
|
||||
|
||||
import type { InputsModelId } from '../../../common/store/inputs/constants';
|
||||
import type { UsersQueryProps } from '../types';
|
||||
import type { NavTab } from '../../../common/components/navigation/types';
|
||||
|
||||
import type { UsersTableType } from '../../store/model';
|
||||
import type { UsersDetailsTableType } from '../../store/model';
|
||||
import type { usersModel } from '../../store';
|
||||
|
||||
interface UsersDetailsComponentReduxProps {
|
||||
query: Query;
|
||||
filters: Filter[];
|
||||
}
|
||||
|
||||
interface UserBodyComponentDispatchProps {
|
||||
setAbsoluteRangeDatePicker: ActionCreator<{
|
||||
id: InputsModelId;
|
||||
|
@ -24,22 +31,22 @@ interface UserBodyComponentDispatchProps {
|
|||
usersDetailsPagePath: string;
|
||||
}
|
||||
|
||||
interface UsersDetailsComponentDispatchProps extends UserBodyComponentDispatchProps {
|
||||
setUsersDetailsTablesActivePageToZero: ActionCreator<null>;
|
||||
}
|
||||
|
||||
export interface UsersDetailsProps {
|
||||
detailName: string;
|
||||
usersDetailsPagePath: string;
|
||||
}
|
||||
|
||||
export type KeyUsersDetailsNavTabWithoutMlPermission = UsersTableType.events &
|
||||
UsersTableType.alerts;
|
||||
export type UsersDetailsComponentProps = UsersDetailsComponentReduxProps &
|
||||
UsersDetailsComponentDispatchProps &
|
||||
UsersQueryProps;
|
||||
|
||||
type KeyUsersDetailsNavTabWithMlPermission = KeyUsersDetailsNavTabWithoutMlPermission &
|
||||
UsersTableType.anomalies;
|
||||
type KeyUsersDetailsNavTab = `${UsersDetailsTableType}`;
|
||||
|
||||
type KeyUsersDetailsNavTab =
|
||||
| KeyUsersDetailsNavTabWithoutMlPermission
|
||||
| KeyUsersDetailsNavTabWithMlPermission;
|
||||
|
||||
export type UsersDetailsNavTab = Record<KeyUsersDetailsNavTab, NavTab>;
|
||||
export type UsersDetailsNavTab = Partial<Record<KeyUsersDetailsNavTab, NavTab>>;
|
||||
|
||||
export type UsersDetailsTabsProps = UserBodyComponentDispatchProps &
|
||||
UsersQueryProps & {
|
||||
|
@ -49,3 +56,9 @@ export type UsersDetailsTabsProps = UserBodyComponentDispatchProps &
|
|||
indexPattern: DataViewBase;
|
||||
type: usersModel.UsersType;
|
||||
};
|
||||
|
||||
export type SetAbsoluteRangeDatePicker = ActionCreator<{
|
||||
id: InputsModelId;
|
||||
from: string;
|
||||
to: string;
|
||||
}>;
|
||||
|
|
|
@ -25,7 +25,6 @@ const TabNameMappedToI18nKey: Record<UsersTableType, string> = {
|
|||
[UsersTableType.anomalies]: i18n.NAVIGATION_ANOMALIES_TITLE,
|
||||
[UsersTableType.risk]: i18n.NAVIGATION_RISK_TITLE,
|
||||
[UsersTableType.events]: i18n.NAVIGATION_EVENTS_TITLE,
|
||||
[UsersTableType.alerts]: i18n.NAVIGATION_ALERTS_TITLE,
|
||||
[UsersTableType.risk]: i18n.NAVIGATION_RISK_TITLE,
|
||||
};
|
||||
|
||||
|
|
|
@ -44,12 +44,6 @@ export const navTabsUsers = (
|
|||
href: getTabsOnUsersUrl(UsersTableType.events),
|
||||
disabled: false,
|
||||
},
|
||||
[UsersTableType.alerts]: {
|
||||
id: UsersTableType.alerts,
|
||||
name: i18n.NAVIGATION_ALERTS_TITLE,
|
||||
href: getTabsOnUsersUrl(UsersTableType.alerts),
|
||||
disabled: false,
|
||||
},
|
||||
[UsersTableType.risk]: {
|
||||
id: UsersTableType.risk,
|
||||
name: i18n.NAVIGATION_RISK_TITLE,
|
||||
|
|
|
@ -10,16 +10,9 @@ import type { GlobalTimeArgs } from '../../../common/containers/use_global_time'
|
|||
import type { ESTermQuery } from '../../../../common/typed_json';
|
||||
import type { NavTab } from '../../../common/components/navigation/types';
|
||||
|
||||
type KeyUsersNavTabWithoutMlPermission = UsersTableType.allUsers &
|
||||
UsersTableType.risk &
|
||||
UsersTableType.events &
|
||||
UsersTableType.alerts;
|
||||
type KeyUsersNavTab = `${UsersTableType}`;
|
||||
|
||||
type KeyUsersNavTabWithMlPermission = KeyUsersNavTabWithoutMlPermission & UsersTableType.anomalies;
|
||||
|
||||
type KeyUsersNavTab = KeyUsersNavTabWithoutMlPermission | KeyUsersNavTabWithMlPermission;
|
||||
|
||||
export type UsersNavTab = Record<KeyUsersNavTab, NavTab>;
|
||||
export type UsersNavTab = Partial<Record<KeyUsersNavTab, NavTab>>;
|
||||
export interface QueryTabBodyProps {
|
||||
type: UsersType;
|
||||
startDate: GlobalTimeArgs['from'];
|
||||
|
|
|
@ -46,13 +46,6 @@ export const NAVIGATION_EVENTS_TITLE = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const NAVIGATION_ALERTS_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.users.navigation.alertsTitle',
|
||||
{
|
||||
defaultMessage: 'External alerts',
|
||||
}
|
||||
);
|
||||
|
||||
export const USER_RISK_SCORE_OVER_TIME = i18n.translate(
|
||||
'xpack.securitySolution.users.navigation.userScoreOverTimeTitle',
|
||||
{
|
||||
|
|
|
@ -90,7 +90,7 @@ const UsersComponent = () => {
|
|||
|
||||
const { tabName } = useParams<{ tabName: string }>();
|
||||
const tabsFilters: Filter[] = React.useMemo(() => {
|
||||
if (tabName === UsersTableType.alerts || tabName === UsersTableType.events) {
|
||||
if (tabName === UsersTableType.events) {
|
||||
return filters.length > 0 ? [...filters, ...userNameExistsFilter] : userNameExistsFilter;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,9 +20,8 @@ import { scoreIntervalToDateTime } from '../../common/components/ml/score/score_
|
|||
import type { UpdateDateRange } from '../../common/components/charts/common';
|
||||
|
||||
import { UserRiskScoreQueryTabBody } from './navigation/user_risk_score_tab_body';
|
||||
import { EventsQueryTabBody } from '../../common/components/events_tab/events_query_tab_body';
|
||||
import { EventsQueryTabBody } from '../../common/components/events_tab';
|
||||
import { TimelineId } from '../../../common/types';
|
||||
import { AlertsView } from '../../common/components/alerts_viewer';
|
||||
|
||||
export const UsersTabs = memo<UsersTabsProps>(
|
||||
({
|
||||
|
@ -94,16 +93,8 @@ export const UsersTabs = memo<UsersTabsProps>(
|
|||
<Route path={`${USERS_PATH}/:tabName(${UsersTableType.events})`}>
|
||||
<EventsQueryTabBody
|
||||
{...tabProps}
|
||||
timelineId={TimelineId.usersPageEvents}
|
||||
pageFilters={pageFilters}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={`${USERS_PATH}/:tabName(${UsersTableType.alerts})`}>
|
||||
<AlertsView
|
||||
entityType="events"
|
||||
timelineId={TimelineId.usersPageExternalAlerts}
|
||||
pageFilters={pageFilters ?? []}
|
||||
{...tabProps}
|
||||
timelineId={TimelineId.usersPageEvents}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
|
|
|
@ -19,7 +19,13 @@ export enum UsersTableType {
|
|||
anomalies = 'anomalies',
|
||||
risk = 'userRisk',
|
||||
events = 'events',
|
||||
alerts = 'externalAlerts',
|
||||
}
|
||||
|
||||
export enum UsersDetailsTableType {
|
||||
authentications = 'authentications',
|
||||
anomalies = 'anomalies',
|
||||
risk = 'userRisk',
|
||||
events = 'events',
|
||||
}
|
||||
|
||||
export interface BasicQueryPaginated {
|
||||
|
@ -42,13 +48,11 @@ export interface UsersQueries {
|
|||
[UsersTableType.anomalies]: null | undefined;
|
||||
[UsersTableType.risk]: UsersRiskScoreQuery;
|
||||
[UsersTableType.events]: BasicQueryPaginated;
|
||||
[UsersTableType.alerts]: BasicQueryPaginated;
|
||||
}
|
||||
|
||||
export interface UserDetailsQueries {
|
||||
[UsersTableType.anomalies]: null | undefined;
|
||||
[UsersTableType.events]: BasicQueryPaginated;
|
||||
[UsersTableType.alerts]: BasicQueryPaginated;
|
||||
}
|
||||
|
||||
export interface UsersPageModel {
|
||||
|
|
|
@ -51,10 +51,6 @@ export const initialUsersState: UsersModel = {
|
|||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
},
|
||||
[UsersTableType.alerts]: {
|
||||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
},
|
||||
},
|
||||
},
|
||||
details: {
|
||||
|
@ -64,10 +60,6 @@ export const initialUsersState: UsersModel = {
|
|||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
},
|
||||
[UsersTableType.alerts]: {
|
||||
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -311,13 +311,11 @@ export type SavedTimelineNote = runtimeTypes.TypeOf<typeof SavedTimelineRuntimeT
|
|||
|
||||
export enum TimelineId {
|
||||
usersPageEvents = 'users-page-events',
|
||||
usersPageExternalAlerts = 'users-page-external-alerts',
|
||||
hostsPageEvents = 'hosts-page-events',
|
||||
hostsPageExternalAlerts = 'hosts-page-external-alerts',
|
||||
networkPageEvents = 'network-page-events',
|
||||
hostsPageSessions = 'hosts-page-sessions-v2',
|
||||
detectionsRulesDetailsPage = 'detections-rules-details-page',
|
||||
detectionsPage = 'detections-page',
|
||||
networkPageExternalAlerts = 'network-page-external-alerts',
|
||||
active = 'timeline-1',
|
||||
casePage = 'timeline-case',
|
||||
test = 'test', // Reserved for testing purposes
|
||||
|
@ -328,11 +326,11 @@ export enum TimelineId {
|
|||
|
||||
export const TimelineIdLiteralRt = runtimeTypes.union([
|
||||
runtimeTypes.literal(TimelineId.hostsPageEvents),
|
||||
runtimeTypes.literal(TimelineId.hostsPageExternalAlerts),
|
||||
runtimeTypes.literal(TimelineId.usersPageEvents),
|
||||
runtimeTypes.literal(TimelineId.networkPageEvents),
|
||||
runtimeTypes.literal(TimelineId.hostsPageSessions),
|
||||
runtimeTypes.literal(TimelineId.detectionsRulesDetailsPage),
|
||||
runtimeTypes.literal(TimelineId.detectionsPage),
|
||||
runtimeTypes.literal(TimelineId.networkPageExternalAlerts),
|
||||
runtimeTypes.literal(TimelineId.active),
|
||||
runtimeTypes.literal(TimelineId.test),
|
||||
runtimeTypes.literal(TimelineId.rulePreview),
|
||||
|
|
|
@ -43,13 +43,11 @@ export interface TimelineState {
|
|||
|
||||
export enum TimelineId {
|
||||
usersPageEvents = 'users-page-events',
|
||||
usersPageExternalAlerts = 'users-page-external-alerts',
|
||||
hostsPageEvents = 'hosts-page-events',
|
||||
hostsPageExternalAlerts = 'hosts-page-external-alerts',
|
||||
networkPageEvents = 'network-page-events',
|
||||
hostsPageSessions = 'hosts-page-sessions-v2',
|
||||
detectionsRulesDetailsPage = 'detections-rules-details-page',
|
||||
detectionsPage = 'detections-page',
|
||||
networkPageExternalAlerts = 'network-page-external-alerts',
|
||||
active = 'timeline-1',
|
||||
casePage = 'timeline-case',
|
||||
test = 'test', // Reserved for testing purposes
|
||||
|
|
|
@ -24067,15 +24067,7 @@
|
|||
"xpack.securitySolution.alerts.severityMapping.severityTitle": "Sévérité par défaut",
|
||||
"xpack.securitySolution.alerts.severityMapping.sourceFieldTitle": "Champ source",
|
||||
"xpack.securitySolution.alerts.severityMapping.sourceValueTitle": "Valeur source",
|
||||
"xpack.securitySolution.alertsView.alertsDocumentType": "Alertes externes",
|
||||
"xpack.securitySolution.alertsView.alertsGraphTitle": "Tendance d'alerte externe",
|
||||
"xpack.securitySolution.alertsView.alertsTableTitle": "Alertes externes",
|
||||
"xpack.securitySolution.alertsView.categoryLabel": "catégorie",
|
||||
"xpack.securitySolution.alertsView.errorFetchingAlertsData": "Impossible d'interroger les données d'alertes",
|
||||
"xpack.securitySolution.alertsView.osqueryAlertTitle": "Exécuter Osquery",
|
||||
"xpack.securitySolution.alertsView.showing": "Affichage",
|
||||
"xpack.securitySolution.alertsView.totalCountOfAlerts": "alertes externes",
|
||||
"xpack.securitySolution.alertsView.unit": "{totalCount, plural, =1 {alerte externe} other {alertes externes}}",
|
||||
"xpack.securitySolution.allHost.errorSearchDescription": "Une erreur s'est produite lors de la recherche de tous les hôtes",
|
||||
"xpack.securitySolution.allHost.failSearchDescription": "Impossible de lancer une recherche sur tous les hôtes",
|
||||
"xpack.securitySolution.andOrBadge.and": "AND",
|
||||
|
@ -24101,7 +24093,6 @@
|
|||
"xpack.securitySolution.appLinks.hosts": "Hôtes",
|
||||
"xpack.securitySolution.appLinks.hosts.anomalies": "Anomalies",
|
||||
"xpack.securitySolution.appLinks.hosts.events": "Événements",
|
||||
"xpack.securitySolution.appLinks.hosts.externalAlerts": "Alertes externes",
|
||||
"xpack.securitySolution.appLinks.hosts.risk": "Risque de l'hôte",
|
||||
"xpack.securitySolution.appLinks.hosts.sessions": "Sessions",
|
||||
"xpack.securitySolution.appLinks.hosts.uncommonProcesses": "Processus inhabituels",
|
||||
|
@ -24109,7 +24100,6 @@
|
|||
"xpack.securitySolution.appLinks.network": "Réseau",
|
||||
"xpack.securitySolution.appLinks.network.description": "Fournit des indicateurs d'activités clés sur une carte interactive ainsi que des tableaux d'événements qui permettent l'interaction avec la chronologie.",
|
||||
"xpack.securitySolution.appLinks.network.dns": "DNS",
|
||||
"xpack.securitySolution.appLinks.network.externalAlerts": "Alertes externes",
|
||||
"xpack.securitySolution.appLinks.network.http": "HTTP",
|
||||
"xpack.securitySolution.appLinks.network.tls": "TLS",
|
||||
"xpack.securitySolution.appLinks.overview": "Aperçu",
|
||||
|
@ -24125,7 +24115,6 @@
|
|||
"xpack.securitySolution.appLinks.users.authentications": "Authentifications",
|
||||
"xpack.securitySolution.appLinks.users.description": "Aperçu complet des données d'utilisateur qui permettent de comprendre l'authentification et le comportement de l'utilisateur dans votre environnement.",
|
||||
"xpack.securitySolution.appLinks.users.events": "Événements",
|
||||
"xpack.securitySolution.appLinks.users.externalAlerts": "Alertes externes",
|
||||
"xpack.securitySolution.appLinks.users.risk": "Risque de l'utilisateur",
|
||||
"xpack.securitySolution.artifactCard.comments.label.hide": "Masquer les commentaires ({count})",
|
||||
"xpack.securitySolution.artifactCard.comments.label.show": "Afficher les commentaires ({count})",
|
||||
|
@ -26554,7 +26543,6 @@
|
|||
"xpack.securitySolution.hosts.hostScoreOverTime.riskScore": "Score de risque",
|
||||
"xpack.securitySolution.hosts.hostScoreOverTime.riskyLabel": "À risque",
|
||||
"xpack.securitySolution.hosts.hostScoreOverTime.riskyThresholdHeader": "Seuil du niveau À risque",
|
||||
"xpack.securitySolution.hosts.navigation.alertsTitle": "Alertes externes",
|
||||
"xpack.securitySolution.hosts.navigation.allHostsTitle": "Tous les hôtes",
|
||||
"xpack.securitySolution.hosts.navigation.anomaliesTitle": "Anomalies",
|
||||
"xpack.securitySolution.hosts.navigation.authenticationsTitle": "Authentifications",
|
||||
|
@ -26804,7 +26792,6 @@
|
|||
"xpack.securitySolution.network.ipDetails.usersTable.rows": "{numRows} {numRows, plural, =0 {ligne} =1 {ligne} other {lignes}}",
|
||||
"xpack.securitySolution.network.ipDetails.usersTable.unit": "{totalCount, plural, =1 {utilisateur} other {utilisateurs}}",
|
||||
"xpack.securitySolution.network.ipDetails.usersTable.usersTitle": "Utilisateurs",
|
||||
"xpack.securitySolution.network.navigation.alertsTitle": "Alertes externes",
|
||||
"xpack.securitySolution.network.navigation.anomaliesTitle": "Anomalies",
|
||||
"xpack.securitySolution.network.navigation.dnsTitle": "DNS",
|
||||
"xpack.securitySolution.network.navigation.flowsTitle": "Flux",
|
||||
|
@ -27131,7 +27118,6 @@
|
|||
"xpack.securitySolution.search.hosts": "Hôtes",
|
||||
"xpack.securitySolution.search.hosts.anomalies": "Anomalies",
|
||||
"xpack.securitySolution.search.hosts.events": "Événements",
|
||||
"xpack.securitySolution.search.hosts.externalAlerts": "Alertes externes",
|
||||
"xpack.securitySolution.search.hosts.risk": "Risque de l'hôte",
|
||||
"xpack.securitySolution.search.hosts.sessions": "Sessions",
|
||||
"xpack.securitySolution.search.hosts.uncommonProcesses": "Processus inhabituels",
|
||||
|
@ -27139,7 +27125,6 @@
|
|||
"xpack.securitySolution.search.manage": "Gérer",
|
||||
"xpack.securitySolution.search.network": "Réseau",
|
||||
"xpack.securitySolution.search.network.dns": "DNS",
|
||||
"xpack.securitySolution.search.network.externalAlerts": "Alertes externes",
|
||||
"xpack.securitySolution.search.network.http": "HTTP",
|
||||
"xpack.securitySolution.search.network.tls": "TLS",
|
||||
"xpack.securitySolution.search.overview": "Aperçu",
|
||||
|
@ -27150,7 +27135,6 @@
|
|||
"xpack.securitySolution.search.users.anomalies": "Anomalies",
|
||||
"xpack.securitySolution.search.users.authentications": "Authentifications",
|
||||
"xpack.securitySolution.search.users.events": "Événements",
|
||||
"xpack.securitySolution.search.users.externalAlerts": "Alertes externes",
|
||||
"xpack.securitySolution.search.users.risk": "Risque de l'utilisateur",
|
||||
"xpack.securitySolution.searchStrategy.error": "Impossible d'exécuter la recherche : {factoryQueryType}",
|
||||
"xpack.securitySolution.sessionsView.columnEntrySourceIp": "IP source",
|
||||
|
@ -27505,7 +27489,6 @@
|
|||
"xpack.securitySolution.user.details.overview.userRiskScoreTitle": "Score de risque de l'utilisateur",
|
||||
"xpack.securitySolution.user.ipDetails.ipOverview.lastSeenTitle": "Vu en dernier",
|
||||
"xpack.securitySolution.userDetails.failSearchDescription": "Impossible de lancer la recherche sur les détails de l'utilisateur",
|
||||
"xpack.securitySolution.users.navigation.alertsTitle": "Alertes externes",
|
||||
"xpack.securitySolution.users.navigation.allUsersTitle": "Tous les utilisateurs",
|
||||
"xpack.securitySolution.users.navigation.anomaliesTitle": "Anomalies",
|
||||
"xpack.securitySolution.users.navigation.authenticationsTitle": "Authentifications",
|
||||
|
|
|
@ -24147,15 +24147,7 @@
|
|||
"xpack.securitySolution.alerts.severityMapping.severityTitle": "デフォルト重要度",
|
||||
"xpack.securitySolution.alerts.severityMapping.sourceFieldTitle": "ソースフィールド",
|
||||
"xpack.securitySolution.alerts.severityMapping.sourceValueTitle": "ソース値",
|
||||
"xpack.securitySolution.alertsView.alertsDocumentType": "外部アラート",
|
||||
"xpack.securitySolution.alertsView.alertsGraphTitle": "外部アラート傾向",
|
||||
"xpack.securitySolution.alertsView.alertsTableTitle": "外部アラート",
|
||||
"xpack.securitySolution.alertsView.categoryLabel": "カテゴリー",
|
||||
"xpack.securitySolution.alertsView.errorFetchingAlertsData": "アラートデータをクエリできませんでした",
|
||||
"xpack.securitySolution.alertsView.osqueryAlertTitle": "Osqueryの実行",
|
||||
"xpack.securitySolution.alertsView.showing": "表示中",
|
||||
"xpack.securitySolution.alertsView.totalCountOfAlerts": "外部アラート",
|
||||
"xpack.securitySolution.alertsView.unit": "外部{totalCount, plural, other {アラート}}",
|
||||
"xpack.securitySolution.allHost.errorSearchDescription": "すべてのホスト検索でエラーが発生しました",
|
||||
"xpack.securitySolution.allHost.failSearchDescription": "すべてのホストで検索を実行できませんでした",
|
||||
"xpack.securitySolution.andOrBadge.and": "AND",
|
||||
|
@ -24181,7 +24173,6 @@
|
|||
"xpack.securitySolution.appLinks.hosts": "ホスト",
|
||||
"xpack.securitySolution.appLinks.hosts.anomalies": "異常",
|
||||
"xpack.securitySolution.appLinks.hosts.events": "イベント",
|
||||
"xpack.securitySolution.appLinks.hosts.externalAlerts": "外部アラート",
|
||||
"xpack.securitySolution.appLinks.hosts.risk": "ホストリスク",
|
||||
"xpack.securitySolution.appLinks.hosts.sessions": "セッション",
|
||||
"xpack.securitySolution.appLinks.hosts.uncommonProcesses": "非共通プロセス",
|
||||
|
@ -24189,7 +24180,6 @@
|
|||
"xpack.securitySolution.appLinks.network": "ネットワーク",
|
||||
"xpack.securitySolution.appLinks.network.description": "インタラクティブなマップで主要なアクティビティメトリックと、タイムラインと連携できるイベントテーブルを提供します。",
|
||||
"xpack.securitySolution.appLinks.network.dns": "DNS",
|
||||
"xpack.securitySolution.appLinks.network.externalAlerts": "外部アラート",
|
||||
"xpack.securitySolution.appLinks.network.http": "HTTP",
|
||||
"xpack.securitySolution.appLinks.network.tls": "TLS",
|
||||
"xpack.securitySolution.appLinks.overview": "概要",
|
||||
|
@ -24205,7 +24195,6 @@
|
|||
"xpack.securitySolution.appLinks.users.authentications": "認証",
|
||||
"xpack.securitySolution.appLinks.users.description": "ユーザーデータの概要を包括的に捉え、環境内の認証およびユーザー動作を把握できます。",
|
||||
"xpack.securitySolution.appLinks.users.events": "イベント",
|
||||
"xpack.securitySolution.appLinks.users.externalAlerts": "外部アラート",
|
||||
"xpack.securitySolution.appLinks.users.risk": "ユーザーリスク",
|
||||
"xpack.securitySolution.artifactCard.comments.label.hide": "コメントを非表示({count})",
|
||||
"xpack.securitySolution.artifactCard.comments.label.show": "コメントを表示({count})",
|
||||
|
@ -26634,7 +26623,6 @@
|
|||
"xpack.securitySolution.hosts.hostScoreOverTime.riskScore": "リスクスコア",
|
||||
"xpack.securitySolution.hosts.hostScoreOverTime.riskyLabel": "高リスク",
|
||||
"xpack.securitySolution.hosts.hostScoreOverTime.riskyThresholdHeader": "高リスクしきい値",
|
||||
"xpack.securitySolution.hosts.navigation.alertsTitle": "外部アラート",
|
||||
"xpack.securitySolution.hosts.navigation.allHostsTitle": "すべてのホスト",
|
||||
"xpack.securitySolution.hosts.navigation.anomaliesTitle": "異常",
|
||||
"xpack.securitySolution.hosts.navigation.authenticationsTitle": "認証",
|
||||
|
@ -26884,7 +26872,6 @@
|
|||
"xpack.securitySolution.network.ipDetails.usersTable.rows": "{numRows} {numRows, plural, other {行}}",
|
||||
"xpack.securitySolution.network.ipDetails.usersTable.unit": "{totalCount, plural, other {ユーザー}}",
|
||||
"xpack.securitySolution.network.ipDetails.usersTable.usersTitle": "ユーザー",
|
||||
"xpack.securitySolution.network.navigation.alertsTitle": "外部アラート",
|
||||
"xpack.securitySolution.network.navigation.anomaliesTitle": "異常",
|
||||
"xpack.securitySolution.network.navigation.dnsTitle": "DNS",
|
||||
"xpack.securitySolution.network.navigation.flowsTitle": "Flow",
|
||||
|
@ -27211,7 +27198,6 @@
|
|||
"xpack.securitySolution.search.hosts": "ホスト",
|
||||
"xpack.securitySolution.search.hosts.anomalies": "異常",
|
||||
"xpack.securitySolution.search.hosts.events": "イベント",
|
||||
"xpack.securitySolution.search.hosts.externalAlerts": "外部アラート",
|
||||
"xpack.securitySolution.search.hosts.risk": "ホストリスク",
|
||||
"xpack.securitySolution.search.hosts.sessions": "セッション",
|
||||
"xpack.securitySolution.search.hosts.uncommonProcesses": "非共通プロセス",
|
||||
|
@ -27219,7 +27205,6 @@
|
|||
"xpack.securitySolution.search.manage": "管理",
|
||||
"xpack.securitySolution.search.network": "ネットワーク",
|
||||
"xpack.securitySolution.search.network.dns": "DNS",
|
||||
"xpack.securitySolution.search.network.externalAlerts": "外部アラート",
|
||||
"xpack.securitySolution.search.network.http": "HTTP",
|
||||
"xpack.securitySolution.search.network.tls": "TLS",
|
||||
"xpack.securitySolution.search.overview": "概要",
|
||||
|
@ -27230,7 +27215,6 @@
|
|||
"xpack.securitySolution.search.users.anomalies": "異常",
|
||||
"xpack.securitySolution.search.users.authentications": "認証",
|
||||
"xpack.securitySolution.search.users.events": "イベント",
|
||||
"xpack.securitySolution.search.users.externalAlerts": "外部アラート",
|
||||
"xpack.securitySolution.search.users.risk": "ユーザーリスク",
|
||||
"xpack.securitySolution.searchStrategy.error": "検索を実行できませんでした:{factoryQueryType}",
|
||||
"xpack.securitySolution.sessionsView.columnEntrySourceIp": "ソース IP",
|
||||
|
@ -27585,7 +27569,6 @@
|
|||
"xpack.securitySolution.user.details.overview.userRiskScoreTitle": "ユーザーリスクスコア",
|
||||
"xpack.securitySolution.user.ipDetails.ipOverview.lastSeenTitle": "前回の認識",
|
||||
"xpack.securitySolution.userDetails.failSearchDescription": "ユーザー詳細で検索を実行できませんでした",
|
||||
"xpack.securitySolution.users.navigation.alertsTitle": "外部アラート",
|
||||
"xpack.securitySolution.users.navigation.allUsersTitle": "すべてのユーザー",
|
||||
"xpack.securitySolution.users.navigation.anomaliesTitle": "異常",
|
||||
"xpack.securitySolution.users.navigation.authenticationsTitle": "認証",
|
||||
|
|
|
@ -24173,15 +24173,7 @@
|
|||
"xpack.securitySolution.alerts.severityMapping.severityTitle": "默认严重性",
|
||||
"xpack.securitySolution.alerts.severityMapping.sourceFieldTitle": "源字段",
|
||||
"xpack.securitySolution.alerts.severityMapping.sourceValueTitle": "源值",
|
||||
"xpack.securitySolution.alertsView.alertsDocumentType": "外部告警",
|
||||
"xpack.securitySolution.alertsView.alertsGraphTitle": "外部告警趋势",
|
||||
"xpack.securitySolution.alertsView.alertsTableTitle": "外部告警",
|
||||
"xpack.securitySolution.alertsView.categoryLabel": "类别",
|
||||
"xpack.securitySolution.alertsView.errorFetchingAlertsData": "无法查询告警数据",
|
||||
"xpack.securitySolution.alertsView.osqueryAlertTitle": "运行 Osquery",
|
||||
"xpack.securitySolution.alertsView.showing": "正在显示",
|
||||
"xpack.securitySolution.alertsView.totalCountOfAlerts": "个外部告警",
|
||||
"xpack.securitySolution.alertsView.unit": "个外部{totalCount, plural, other {告警}}",
|
||||
"xpack.securitySolution.allHost.errorSearchDescription": "搜索所有主机时发生错误",
|
||||
"xpack.securitySolution.allHost.failSearchDescription": "无法对所有主机执行搜索",
|
||||
"xpack.securitySolution.andOrBadge.and": "AND",
|
||||
|
@ -24207,7 +24199,6 @@
|
|||
"xpack.securitySolution.appLinks.hosts": "主机",
|
||||
"xpack.securitySolution.appLinks.hosts.anomalies": "异常",
|
||||
"xpack.securitySolution.appLinks.hosts.events": "事件",
|
||||
"xpack.securitySolution.appLinks.hosts.externalAlerts": "外部告警",
|
||||
"xpack.securitySolution.appLinks.hosts.risk": "主机风险",
|
||||
"xpack.securitySolution.appLinks.hosts.sessions": "会话",
|
||||
"xpack.securitySolution.appLinks.hosts.uncommonProcesses": "不常见进程",
|
||||
|
@ -24215,7 +24206,6 @@
|
|||
"xpack.securitySolution.appLinks.network": "网络",
|
||||
"xpack.securitySolution.appLinks.network.description": "在交互式地图中提供关键活动指标以及启用与时间线进行交互的事件表。",
|
||||
"xpack.securitySolution.appLinks.network.dns": "DNS",
|
||||
"xpack.securitySolution.appLinks.network.externalAlerts": "外部告警",
|
||||
"xpack.securitySolution.appLinks.network.http": "HTTP",
|
||||
"xpack.securitySolution.appLinks.network.tls": "TLS",
|
||||
"xpack.securitySolution.appLinks.overview": "概览",
|
||||
|
@ -24231,7 +24221,6 @@
|
|||
"xpack.securitySolution.appLinks.users.authentications": "身份验证",
|
||||
"xpack.securitySolution.appLinks.users.description": "用户数据的全面概览,这些数据有助于了解您环境中的身份验证和用户行为。",
|
||||
"xpack.securitySolution.appLinks.users.events": "事件",
|
||||
"xpack.securitySolution.appLinks.users.externalAlerts": "外部告警",
|
||||
"xpack.securitySolution.appLinks.users.risk": "用户风险",
|
||||
"xpack.securitySolution.artifactCard.comments.label.hide": "隐藏注释 ({count})",
|
||||
"xpack.securitySolution.artifactCard.comments.label.show": "显示注释 ({count})",
|
||||
|
@ -26661,7 +26650,6 @@
|
|||
"xpack.securitySolution.hosts.hostScoreOverTime.riskScore": "风险分数",
|
||||
"xpack.securitySolution.hosts.hostScoreOverTime.riskyLabel": "有风险",
|
||||
"xpack.securitySolution.hosts.hostScoreOverTime.riskyThresholdHeader": "有风险的阈值",
|
||||
"xpack.securitySolution.hosts.navigation.alertsTitle": "外部告警",
|
||||
"xpack.securitySolution.hosts.navigation.allHostsTitle": "所有主机",
|
||||
"xpack.securitySolution.hosts.navigation.anomaliesTitle": "异常",
|
||||
"xpack.securitySolution.hosts.navigation.authenticationsTitle": "身份验证",
|
||||
|
@ -26911,7 +26899,6 @@
|
|||
"xpack.securitySolution.network.ipDetails.usersTable.rows": "{numRows} {numRows, plural, other {行}}",
|
||||
"xpack.securitySolution.network.ipDetails.usersTable.unit": "{totalCount, plural, other {个用户}}",
|
||||
"xpack.securitySolution.network.ipDetails.usersTable.usersTitle": "用户",
|
||||
"xpack.securitySolution.network.navigation.alertsTitle": "外部告警",
|
||||
"xpack.securitySolution.network.navigation.anomaliesTitle": "异常",
|
||||
"xpack.securitySolution.network.navigation.dnsTitle": "DNS",
|
||||
"xpack.securitySolution.network.navigation.flowsTitle": "流",
|
||||
|
@ -27238,7 +27225,6 @@
|
|||
"xpack.securitySolution.search.hosts": "主机",
|
||||
"xpack.securitySolution.search.hosts.anomalies": "异常",
|
||||
"xpack.securitySolution.search.hosts.events": "事件",
|
||||
"xpack.securitySolution.search.hosts.externalAlerts": "外部告警",
|
||||
"xpack.securitySolution.search.hosts.risk": "主机风险",
|
||||
"xpack.securitySolution.search.hosts.sessions": "会话",
|
||||
"xpack.securitySolution.search.hosts.uncommonProcesses": "不常见进程",
|
||||
|
@ -27246,7 +27232,6 @@
|
|||
"xpack.securitySolution.search.manage": "管理",
|
||||
"xpack.securitySolution.search.network": "网络",
|
||||
"xpack.securitySolution.search.network.dns": "DNS",
|
||||
"xpack.securitySolution.search.network.externalAlerts": "外部告警",
|
||||
"xpack.securitySolution.search.network.http": "HTTP",
|
||||
"xpack.securitySolution.search.network.tls": "TLS",
|
||||
"xpack.securitySolution.search.overview": "概览",
|
||||
|
@ -27257,7 +27242,6 @@
|
|||
"xpack.securitySolution.search.users.anomalies": "异常",
|
||||
"xpack.securitySolution.search.users.authentications": "身份验证",
|
||||
"xpack.securitySolution.search.users.events": "事件",
|
||||
"xpack.securitySolution.search.users.externalAlerts": "外部告警",
|
||||
"xpack.securitySolution.search.users.risk": "用户风险",
|
||||
"xpack.securitySolution.searchStrategy.error": "无法运行搜索:{factoryQueryType}",
|
||||
"xpack.securitySolution.sessionsView.columnEntrySourceIp": "源 IP",
|
||||
|
@ -27612,7 +27596,6 @@
|
|||
"xpack.securitySolution.user.details.overview.userRiskScoreTitle": "用户风险分数",
|
||||
"xpack.securitySolution.user.ipDetails.ipOverview.lastSeenTitle": "最后看到时间",
|
||||
"xpack.securitySolution.userDetails.failSearchDescription": "无法对用户详情执行搜索",
|
||||
"xpack.securitySolution.users.navigation.alertsTitle": "外部告警",
|
||||
"xpack.securitySolution.users.navigation.allUsersTitle": "所有用户",
|
||||
"xpack.securitySolution.users.navigation.anomaliesTitle": "异常",
|
||||
"xpack.securitySolution.users.navigation.authenticationsTitle": "身份验证",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue