Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SIEM] Alerts view - adding alerts table #51959

Merged
merged 37 commits into from
Dec 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6e4e895
add alert view to hosts page
angorayc Nov 24, 2019
43be007
add defaultHeaders
angorayc Nov 26, 2019
745dad1
add alerts table
angorayc Nov 28, 2019
4b7ea70
fix dsl query
angorayc Nov 29, 2019
1b153d8
add alerts histogram
angorayc Dec 4, 2019
ee3aca7
Merge remote-tracking branch 'upstream/master' into alert-view
angorayc Dec 6, 2019
16a0306
Merge remote-tracking branch 'upstream/master' into alert-view
angorayc Dec 9, 2019
d605ffb
add i18n for alerts table
angorayc Dec 9, 2019
56be243
fix types error
angorayc Dec 11, 2019
6140dd2
fix type issue
angorayc Dec 12, 2019
c9f00a7
Merge remote-tracking branch 'upstream/master' into alert-view
angorayc Dec 12, 2019
71ef4a0
whitespace cleanup
patrykkopycinski Dec 12, 2019
2f45923
fix types
patrykkopycinski Dec 12, 2019
5044662
fix types
patrykkopycinski Dec 12, 2019
4fd84dc
Merge branch 'alert-view' of github.com:angorayc/kibana into alert-view
angorayc Dec 12, 2019
a9c020e
fix types
angorayc Dec 12, 2019
89823a3
fix types
angorayc Dec 13, 2019
ae8fd5e
fix types
angorayc Dec 13, 2019
592a8ed
rename params
angorayc Dec 13, 2019
001aed3
fix unit test
angorayc Dec 13, 2019
9c40863
fix types
angorayc Dec 13, 2019
1d7b42e
revert change on updateHostsSort
angorayc Dec 14, 2019
69d5476
remove unused prop
angorayc Dec 14, 2019
b2cd515
Merge remote-tracking branch 'upstream/master' into alert-view
angorayc Dec 16, 2019
838c9bc
update unit test
angorayc Dec 16, 2019
f231c4e
pair programming with angela to get filter working
XavierM Dec 17, 2019
5acbff6
update alerts query
angorayc Dec 17, 2019
9479d73
clean up
angorayc Dec 17, 2019
8133ffa
fix queries
angorayc Dec 17, 2019
69d2a87
align type for pageFilters
angorayc Dec 17, 2019
1e22251
apply page filter for network page
angorayc Dec 17, 2019
2eeeb78
Merge branch 'master' into alert-view
elasticmachine Dec 17, 2019
ccc57ff
simplify filter props for alerts view
angorayc Dec 18, 2019
ec44616
clean up
angorayc Dec 18, 2019
c49b214
Merge branch 'alert-view' of github.com:angorayc/kibana into alert-view
angorayc Dec 18, 2019
0de574e
replace hard coded tab name
angorayc Dec 18, 2019
affdcf1
Merge remote-tracking branch 'upstream/master' into alert-view
angorayc Dec 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useMemo } from 'react';

import { esFilters } from '../../../../../../../src/plugins/data/common/es_query';
import { StatefulEventsViewer } from '../events_viewer';
import * as i18n from './translations';
import { alertsDefaultModel } from './default_headers';

export interface OwnProps {
end: number;
id: string;
start: number;
}

const ALERTS_TABLE_ID = 'timeline-alerts-table';
const defaultAlertsFilters: esFilters.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,
},
},
],
},
},
},
];

export const AlertsTable = React.memo(
({
endDate,
startDate,
pageFilters = [],
}: {
endDate: number;
startDate: number;
pageFilters?: esFilters.Filter[];
}) => {
const alertsFilter = useMemo(() => [...defaultAlertsFilters, ...pageFilters], [pageFilters]);
return (
<StatefulEventsViewer
defaultFilters={alertsFilter}
defaultModel={alertsDefaultModel}
end={endDate}
id={ALERTS_TABLE_ID}
start={startDate}
timelineTypeContext={useMemo(
() => ({
documentType: i18n.ALERTS_DOCUMENT_TYPE,
footerText: i18n.TOTAL_COUNT_OF_ALERTS,
showCheckboxes: false,
showRowRenderers: false,
title: i18n.ALERTS_TABLE_TITLE,
}),
[]
)}
/>
);
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { ColumnHeader } from '../timeline/body/column_headers/column_header';
import { defaultColumnHeaderType } from '../timeline/body/column_headers/default_headers';
import { DEFAULT_COLUMN_MIN_WIDTH, DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../timeline/body/helpers';
import { timelineDefaults, SubsetTimelineModel } from '../../store/timeline/model';

export const alertsHeaders: ColumnHeader[] = [
{
columnHeaderType: defaultColumnHeaderType,
id: '@timestamp',
width: DEFAULT_DATE_COLUMN_MIN_WIDTH,
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'event.module',
width: DEFAULT_COLUMN_MIN_WIDTH,
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'event.dataset',
width: DEFAULT_COLUMN_MIN_WIDTH,
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'event.category',
width: DEFAULT_COLUMN_MIN_WIDTH,
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'event.severity',
width: DEFAULT_COLUMN_MIN_WIDTH,
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'observer.name',
width: DEFAULT_COLUMN_MIN_WIDTH,
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'host.name',
width: DEFAULT_COLUMN_MIN_WIDTH,
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'message',
width: DEFAULT_COLUMN_MIN_WIDTH,
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'agent.id',
width: DEFAULT_COLUMN_MIN_WIDTH,
},
{
columnHeaderType: defaultColumnHeaderType,
id: 'agent.type',
width: DEFAULT_COLUMN_MIN_WIDTH,
},
];

export const alertsDefaultModel: SubsetTimelineModel = {
...timelineDefaults,
columns: alertsHeaders,
};
Comment on lines +65 to +68
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing to change here, but just a heads up that I ended up moving showCheckboxes and showRowRenderers from the TimelineTypeContext into redux, so when we merge we'll want to add the following to the alertsDefaultModel ( showCheckboxes defaults to false in timelineDefaults, and can be left off)

  showRowRenderers: false,

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { noop } from 'lodash/fp';
import React from 'react';

import { EuiSpacer } from '@elastic/eui';
import { manageQuery } from '../page/manage_query';
import { AlertsOverTimeHistogram } from '../page/hosts/alerts_over_time';
import { AlertsComponentsQueryProps } from './types';
import { AlertsOverTimeQuery } from '../../containers/alerts/alerts_over_time';
import { hostsModel } from '../../store/model';
import { AlertsTable } from './alerts_table';

const AlertsOverTimeManage = manageQuery(AlertsOverTimeHistogram);
export const AlertsView = ({
defaultFilters,
deleteQuery,
endDate,
filterQuery,
pageFilters,
skip,
setQuery,
startDate,
type,
updateDateRange = noop,
}: AlertsComponentsQueryProps) => (
<>
<AlertsOverTimeQuery
endDate={endDate}
filterQuery={filterQuery}
sourceId="default"
startDate={startDate}
type={hostsModel.HostsType.page}
>
angorayc marked this conversation as resolved.
Show resolved Hide resolved
{({ alertsOverTime, loading, id, inspect, refetch, totalCount }) => (
<AlertsOverTimeManage
data={alertsOverTime!}
endDate={endDate}
id={id}
inspect={inspect}
loading={loading}
refetch={refetch}
setQuery={setQuery}
startDate={startDate}
totalCount={totalCount}
updateDateRange={updateDateRange}
/>
)}
</AlertsOverTimeQuery>
<EuiSpacer size="l" />
<AlertsTable endDate={endDate} startDate={startDate} pageFilters={pageFilters} />
</>
);

AlertsView.displayName = 'AlertsView';
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { i18n } from '@kbn/i18n';

export const ALERTS_DOCUMENT_TYPE = i18n.translate('xpack.siem.hosts.alertsDocumentType', {
defaultMessage: 'Alerts',
});

export const TOTAL_COUNT_OF_ALERTS = i18n.translate('xpack.siem.hosts.totalCountOfAlerts', {
defaultMessage: 'alerts match the search criteria',
});

export const ALERTS_TABLE_TITLE = i18n.translate('xpack.siem.hosts.alertsDocumentType', {
defaultMessage: 'Alerts',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { esFilters } from '../../../../../../../src/plugins/data/common';
import { HostsComponentsQueryProps } from '../../pages/hosts/navigation/types';
import { NetworkComponentQueryProps } from '../../pages/network/navigation/types';

type CommonQueryProps = HostsComponentsQueryProps | NetworkComponentQueryProps;
export interface AlertsComponentsQueryProps
extends Pick<
CommonQueryProps,
| 'deleteQuery'
| 'endDate'
| 'filterQuery'
| 'skip'
| 'setQuery'
| 'startDate'
| 'type'
| 'updateDateRange'
> {
pageFilters: esFilters.Filter[];
defaultFilters?: esFilters.Filter[];
XavierM marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { isEqual } from 'lodash/fp';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState, useMemo } from 'react';
import { connect } from 'react-redux';
import { ActionCreator } from 'typescript-fsa';
import chrome from 'ui/chrome';
Expand All @@ -32,6 +32,7 @@ export interface OwnProps {
id: string;
start: number;
headerFilterGroup?: React.ReactNode;
pageFilters?: esFilters.Filter[];
timelineTypeContext?: TimelineTypeContextProps;
utilityBar?: (totalCount: number) => React.ReactNode;
}
Expand Down Expand Up @@ -150,7 +151,7 @@ const StatefulEventsViewerComponent = React.memo<Props>(

const handleOnMouseEnter = useCallback(() => setShowInspect(true), []);
const handleOnMouseLeave = useCallback(() => setShowInspect(false), []);

const eventsFilter = useMemo(() => [...filters], [defaultFilters]);
return (
<div onMouseEnter={handleOnMouseEnter} onMouseLeave={handleOnMouseLeave}>
<EventsViewer
Expand All @@ -159,7 +160,7 @@ const StatefulEventsViewerComponent = React.memo<Props>(
id={id}
dataProviders={dataProviders!}
end={end}
filters={[...filters, ...defaultFilters]}
filters={eventsFilter}
headerFilterGroup={headerFilterGroup}
indexPattern={indexPatterns ?? { fields: [], title: '' }}
isLive={isLive}
Expand Down Expand Up @@ -192,7 +193,8 @@ const StatefulEventsViewerComponent = React.memo<Props>(
isEqual(prevProps.query, nextProps.query) &&
prevProps.pageCount === nextProps.pageCount &&
isEqual(prevProps.sort, nextProps.sort) &&
prevProps.start === nextProps.start
prevProps.start === nextProps.start &&
isEqual(prevProps.defaultFilters, nextProps.defaultFilters)
);

StatefulEventsViewerComponent.displayName = 'StatefulEventsViewerComponent';
Expand Down Expand Up @@ -227,7 +229,7 @@ export const StatefulEventsViewer = connect(makeMapStateToProps, {
createTimeline: timelineActions.createTimeline,
deleteEventQuery: inputsActions.deleteOneQuery,
updateItemsPerPage: timelineActions.updateItemsPerPage,
updateSort: timelineActions.updateSort,
removeColumn: timelineActions.removeColumn,
upsertColumn: timelineActions.upsertColumn,
setSearchBarFilter: inputsActions.setSearchBarFilter,
})(StatefulEventsViewerComponent);
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { encodeIpv6 } from '../../../lib/helpers';

import { getBreadcrumbsForRoute, setBreadcrumbs } from '.';
import { HostsTableType } from '../../../store/hosts/model';
import { RouteSpyState } from '../../../utils/route/types';
import { RouteSpyState, SiemRouteType } from '../../../utils/route/types';
import { TabNavigationProps } from '../tab_navigation/types';
import { NetworkRouteType } from '../../../pages/network/navigation/types';

jest.mock('ui/chrome', () => ({
getBasePath: () => {
Expand All @@ -30,6 +31,17 @@ jest.mock('../../search_bar', () => ({
},
}));

const mockDefaultTab = (pageName: string): SiemRouteType | undefined => {
switch (pageName) {
case 'hosts':
return HostsTableType.authentications;
case 'network':
return NetworkRouteType.flows;
default:
return undefined;
}
};

const getMockObject = (
pageName: string,
pathName: string,
Expand Down Expand Up @@ -69,7 +81,7 @@ const getMockObject = (
pageName,
pathName,
search: '',
tabName: HostsTableType.authentications,
tabName: mockDefaultTab(pageName) as HostsTableType,
query: { query: '', language: 'kuery' },
filters: [],
timeline: {
Expand Down Expand Up @@ -136,6 +148,10 @@ describe('Navigation Breadcrumbs', () => {
href:
'#/link-to/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))',
},
{
text: 'Flows',
href: '',
},
]);
});

Expand Down Expand Up @@ -176,7 +192,11 @@ describe('Navigation Breadcrumbs', () => {
href:
'#/link-to/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))',
},
{ text: '192.0.2.255', href: '' },
{
text: ipv4,
href: `#/link-to/network/ip/${ipv4}?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`,
},
{ text: 'Flows', href: '' },
]);
});

Expand All @@ -189,7 +209,11 @@ describe('Navigation Breadcrumbs', () => {
href:
'#/link-to/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))',
},
{ text: '2001:db8:ffff:ffff:ffff:ffff:ffff:ffff', href: '' },
{
text: ipv6,
href: `#/link-to/network/ip/${ipv6Encoded}?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`,
},
{ text: 'Flows', href: '' },
]);
});
});
Expand Down
Loading