[SIEM] Histogram enhancement (#54544) (#54791)

* generic histogram container

* generic histogram container

* rename params

* fix inspect

* fix update with timerange

* clean up props

* send stackByField to server side

* fix inspect button

* helper node xavier

* fix DNS histogram

* fix DNS query params

* move utils for fetch data into containers

* cleanup graphql template on client side

* rename grqphql data

* i18n

* fix type

* fix i18n

* fix i18n

* fix subtitle

* fix subtitle

* fix i18n

* fix for reviews

* fix types

* remove unused test

* fix integration

Co-authored-by: Xavier Mouligneau <189600+XavierM@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: Xavier Mouligneau <189600+XavierM@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Angela Chuang 2020-01-15 04:46:40 +08:00 committed by GitHub
parent 7038ce28bf
commit f95f3f39b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
80 changed files with 1423 additions and 1625 deletions

View file

@ -3,57 +3,74 @@
* 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 React, { useEffect, useCallback } 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);
import { AlertsComponentsQueryProps } from './types';
import { AlertsTable } from './alerts_table';
import * as i18n from './translations';
import { MatrixHistogramOption } from '../matrix_histogram/types';
import { MatrixHistogramContainer } from '../../containers/matrix_histogram';
import { MatrixHistogramGqlQuery } from '../../containers/matrix_histogram/index.gql_query';
const ID = 'alertsOverTimeQuery';
const alertsStackByOptions: MatrixHistogramOption[] = [
{
text: i18n.ALERTS_STACK_BY_MODULE,
value: 'event.module',
},
];
const dataKey = 'AlertsHistogram';
export const AlertsView = ({
defaultFilters,
deleteQuery,
endDate,
filterQuery,
pageFilters,
skip,
setQuery,
skip,
startDate,
type,
updateDateRange = noop,
}: AlertsComponentsQueryProps) => (
<>
<AlertsOverTimeQuery
endDate={endDate}
filterQuery={filterQuery}
sourceId="default"
startDate={startDate}
type={hostsModel.HostsType.page}
>
{({ 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} />
</>
);
}: AlertsComponentsQueryProps) => {
useEffect(() => {
return () => {
if (deleteQuery) {
deleteQuery({ id: ID });
}
};
}, []);
const getSubtitle = useCallback(
(totalCount: number) => `${i18n.SHOWING}: ${totalCount} ${i18n.UNIT(totalCount)}`,
[]
);
return (
<>
<MatrixHistogramContainer
dataKey={dataKey}
deleteQuery={deleteQuery}
defaultStackByOption={alertsStackByOptions[0]}
endDate={endDate}
errorMessage={i18n.ERROR_FETCHING_ALERTS_DATA}
filterQuery={filterQuery}
id={ID}
isAlertsHistogram={true}
query={MatrixHistogramGqlQuery}
setQuery={setQuery}
skip={skip}
sourceId="default"
stackByOptions={alertsStackByOptions}
startDate={startDate}
subtitle={getSubtitle}
title={i18n.ALERTS_DOCUMENT_TYPE}
type={type}
updateDateRange={updateDateRange}
/>
<EuiSpacer size="l" />
<AlertsTable endDate={endDate} startDate={startDate} pageFilters={pageFilters} />
</>
);
};
AlertsView.displayName = 'AlertsView';

View file

@ -6,14 +6,38 @@
import { i18n } from '@kbn/i18n';
export const ALERTS_DOCUMENT_TYPE = i18n.translate('xpack.siem.hosts.alertsDocumentType', {
export const ALERTS_DOCUMENT_TYPE = i18n.translate('xpack.siem.alertsView.alertsDocumentType', {
defaultMessage: 'Alerts',
});
export const TOTAL_COUNT_OF_ALERTS = i18n.translate('xpack.siem.hosts.totalCountOfAlerts', {
export const TOTAL_COUNT_OF_ALERTS = i18n.translate('xpack.siem.alertsView.totalCountOfAlerts', {
defaultMessage: 'alerts match the search criteria',
});
export const ALERTS_TABLE_TITLE = i18n.translate('xpack.siem.hosts.alertsDocumentType', {
export const ALERTS_TABLE_TITLE = i18n.translate('xpack.siem.alertsView.alertsDocumentType', {
defaultMessage: 'Alerts',
});
export const ALERTS_STACK_BY_MODULE = i18n.translate(
'xpack.siem.alertsView.alertsStackByOptions.module',
{
defaultMessage: 'module',
}
);
export const SHOWING = i18n.translate('xpack.siem.alertsView.showing', {
defaultMessage: 'Showing',
});
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.alertsView.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {alert} other {alerts}}`,
});
export const ERROR_FETCHING_ALERTS_DATA = i18n.translate(
'xpack.siem.alertsView.errorFetchingAlertsData',
{
defaultMessage: 'Failed to query alerts data',
}
);

View file

@ -7,6 +7,7 @@
import { esFilters } from '../../../../../../../src/plugins/data/common';
import { HostsComponentsQueryProps } from '../../pages/hosts/navigation/types';
import { NetworkComponentQueryProps } from '../../pages/network/navigation/types';
import { MatrixHistogramOption } from '../matrix_histogram/types';
type CommonQueryProps = HostsComponentsQueryProps | NetworkComponentQueryProps;
export interface AlertsComponentsQueryProps
@ -22,5 +23,7 @@ export interface AlertsComponentsQueryProps
| 'updateDateRange'
> {
pageFilters: esFilters.Filter[];
stackByOptions?: MatrixHistogramOption[];
defaultFilters?: esFilters.Filter[];
defaultStackByOption?: MatrixHistogramOption;
}

View file

@ -1,30 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { MatrixHistogramBasicProps } from '../matrix_histogram/types';
import { MatrixOverTimeHistogramData } from '../../graphql/types';
import { MatrixHistogram } from '../matrix_histogram';
import * as i18n from './translation';
export const AnomaliesOverTimeHistogram = (
props: MatrixHistogramBasicProps<MatrixOverTimeHistogramData>
) => {
const dataKey = 'anomaliesOverTime';
const { totalCount } = props;
const subtitle = `${i18n.SHOWING}: ${totalCount.toLocaleString()} ${i18n.UNIT(totalCount)}`;
const { ...matrixOverTimeProps } = props;
return (
<MatrixHistogram
title={i18n.ANOMALIES_COUNT_FREQUENCY_BY_ACTION}
subtitle={subtitle}
dataKey={dataKey}
{...matrixOverTimeProps}
/>
);
};

View file

@ -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;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
export const ANOMALIES_COUNT_FREQUENCY_BY_ACTION = i18n.translate(
'xpack.siem.anomaliesOverTime.anomaliesCountFrequencyByJobTile',
{
defaultMessage: 'Anomalies count by job',
}
);
export const SHOWING = i18n.translate('xpack.siem.anomaliesOverTime.showing', {
defaultMessage: 'Showing',
});
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.anomaliesOverTime.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {anomaly} other {anomalies}}`,
});

View file

@ -14,6 +14,7 @@ import {
ScaleType,
SettingsSpecProps,
TickFormatter,
Position,
} from '@elastic/charts';
import styled from 'styled-components';
import { useUiSetting } from '../../lib/kibana';
@ -35,6 +36,7 @@ export interface ChartData {
export interface ChartSeriesConfigs {
customHeight?: number;
customSeriesColors?: string[];
series?: {
xScaleType?: ScaleType | undefined;
yScaleType?: ScaleType | undefined;
@ -105,6 +107,7 @@ export const chartDefaultSettings = {
showLegend: false,
showLegendDisplayValue: false,
debug: false,
legendPosition: Position.Bottom,
};
export const getChartHeight = (customHeight?: number, autoSizerHeight?: number): string => {

View file

@ -10,6 +10,7 @@ import { shallow } from 'enzyme';
import React from 'react';
import { MatrixHistogram } from '.';
import { MatrixHistogramGqlQuery as mockQuery } from '../../containers/matrix_histogram/index.gql_query';
jest.mock('../../lib/kibana');
@ -31,18 +32,27 @@ jest.mock('../charts/barchart', () => {
};
});
describe('Load More Events Table Component', () => {
describe('Matrix Histogram Component', () => {
const mockMatrixOverTimeHistogramProps = {
data: [],
dataKey: 'mockDataKey',
defaultIndex: ['defaultIndex'],
defaultStackByOption: { text: 'text', value: 'value' },
endDate: new Date('2019-07-18T20:00:00.000Z').valueOf(),
errorMessage: 'error',
id: 'mockId',
loading: true,
updateDateRange: () => {},
isInspected: false,
isPtrIncluded: false,
query: mockQuery,
setQuery: jest.fn(),
skip: false,
sourceId: 'default',
stackByField: 'mockStackByField',
stackByOptions: [{ text: 'text', value: 'value' }],
startDate: new Date('2019-07-18T19:00: 00.000Z').valueOf(),
subtitle: 'mockSubtitle',
totalCount: -1,
title: 'mockTitle',
updateDateRange: jest.fn(),
};
describe('rendering', () => {
test('it renders EuiLoadingContent on initialLoad', () => {

View file

@ -4,38 +4,65 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import { ScaleType } from '@elastic/charts';
import darkTheme from '@elastic/eui/dist/eui_theme_dark.json';
import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
import { EuiLoadingContent } from '@elastic/eui';
import { EuiLoadingContent, EuiSelect } from '@elastic/eui';
import { noop } from 'lodash/fp';
import * as i18n from './translations';
import { BarChart } from '../charts/barchart';
import { HeaderSection } from '../header_section';
import { ChartSeriesData } from '../charts/common';
import { DEFAULT_DARK_MODE } from '../../../common/constants';
import { useUiSetting$ } from '../../lib/kibana';
import { Loader } from '../loader';
import { Panel } from '../panel';
import { getBarchartConfigs, getCustomChartData } from '../../components/matrix_histogram/utils';
import { useQuery } from '../../containers/matrix_histogram/utils';
import {
MatrixHistogramProps,
MatrixHistogramOption,
HistogramAggregation,
MatrixHistogramQueryProps,
} from './types';
import { generateTablePaginationOptions } from '../paginated_table/helpers';
import { ChartSeriesData } from '../charts/common';
import { InspectButtonContainer } from '../inspect';
import { getBarchartConfigs, getCustomChartData } from './utils';
import { MatrixHistogramProps, MatrixHistogramDataTypes } from './types';
export const MatrixHistogramComponent: React.FC<MatrixHistogramProps<MatrixHistogramDataTypes>> = ({
data,
export const MatrixHistogramComponent: React.FC<MatrixHistogramProps &
MatrixHistogramQueryProps> = ({
activePage,
dataKey,
defaultStackByOption,
endDate,
errorMessage,
filterQuery,
hideHistogramIfEmpty = false,
id,
loading,
isAlertsHistogram,
isAnomaliesHistogram,
isAuthenticationsHistogram,
isDNSHistogram,
isEventsType,
isPtrIncluded,
isInspected,
legendPosition,
limit,
mapping,
query,
scaleType = ScaleType.Time,
setQuery,
showLegend,
skip,
stackByOptions,
startDate,
subtitle,
title,
totalCount,
updateDateRange,
yTickFormatter,
showLegend,
sort,
}) => {
const barchartConfigs = getBarchartConfigs({
from: startDate,
@ -44,24 +71,117 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramProps<MatrixHisto
scaleType,
yTickFormatter,
showLegend,
legendPosition,
});
const [showInspect, setShowInspect] = useState(false);
const [darkMode] = useUiSetting$<boolean>(DEFAULT_DARK_MODE);
const [loadingInitial, setLoadingInitial] = useState(false);
const barChartData: ChartSeriesData[] = getCustomChartData(data, mapping);
const handleOnMouseEnter = useCallback(() => {
if (!showInspect) {
setShowInspect(true);
}
}, [showInspect, setShowInspect]);
const handleOnMouseLeave = useCallback(() => {
if (showInspect) {
setShowInspect(false);
}
}, [showInspect, setShowInspect]);
const [selectedStackByOption, setSelectedStackByOption] = useState<MatrixHistogramOption>(
defaultStackByOption
);
const [subtitleWithCounts, setSubtitle] = useState<string>('');
const [hideHistogram, setHideHistogram] = useState<boolean>(hideHistogramIfEmpty);
const [barChartData, setBarChartData] = useState<ChartSeriesData[] | null>(null);
const setSelectedChartOptionCallback = useCallback(
(event: React.ChangeEvent<HTMLSelectElement>) => {
setSelectedStackByOption(
stackByOptions?.find(co => co.value === event.target.value) ?? defaultStackByOption
);
},
[]
);
const getPagination = () =>
activePage != null && limit != null
? generateTablePaginationOptions(activePage, limit)
: undefined;
const { data, loading, inspect, totalCount, refetch = noop } = useQuery<{}, HistogramAggregation>(
{
dataKey,
endDate,
errorMessage,
filterQuery,
query,
skip,
startDate,
sort,
title,
isAlertsHistogram,
isAnomaliesHistogram,
isAuthenticationsHistogram,
isDNSHistogram,
isEventsType,
isInspected,
isPtrIncluded,
pagination: useMemo(() => getPagination(), [activePage, limit]),
stackByField: selectedStackByOption.value,
}
);
useEffect(() => {
if (totalCount >= 0 && loadingInitial) {
setLoadingInitial(false);
if (subtitle != null)
setSubtitle(typeof subtitle === 'function' ? subtitle(totalCount) : subtitle);
if (totalCount <= 0) {
if (hideHistogramIfEmpty) {
setHideHistogram(true);
} else {
setHideHistogram(false);
}
} else {
setHideHistogram(false);
}
}, [loading, loadingInitial, totalCount]);
return (
<InspectButtonContainer show={!loadingInitial}>
<Panel data-test-subj={`${dataKey}Panel`} loading={loading}>
<HeaderSection id={id} title={title} subtitle={!loadingInitial && subtitle} />
setBarChartData(getCustomChartData(data, mapping));
{loadingInitial ? (
setQuery({ id, inspect, loading, refetch });
}, [
subtitle,
setSubtitle,
setHideHistogram,
setBarChartData,
setQuery,
hideHistogramIfEmpty,
totalCount,
isInspected,
loading,
data,
]);
return !hideHistogram ? (
<InspectButtonContainer show={showInspect}>
<Panel
data-test-subj={`${id}Panel`}
loading={loading}
onMouseEnter={handleOnMouseEnter}
onMouseLeave={handleOnMouseLeave}
>
<HeaderSection
id={id}
title={title}
subtitle={!loading && (totalCount >= 0 ? subtitleWithCounts : null)}
>
{stackByOptions && (
<EuiSelect
onChange={setSelectedChartOptionCallback}
options={stackByOptions}
prepend={i18n.STACK_BY}
value={selectedStackByOption?.value}
/>
)}
</HeaderSection>
{loading ? (
<EuiLoadingContent data-test-subj="initialLoadingPanelMatrixOverTime" lines={10} />
) : (
<>
@ -80,7 +200,7 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramProps<MatrixHisto
)}
</Panel>
</InspectButtonContainer>
);
) : null;
};
export const MatrixHistogram = React.memo(MatrixHistogramComponent);

View file

@ -6,6 +6,9 @@
import { i18n } from '@kbn/i18n';
export const NETWORK_DNS_HISTOGRAM = i18n.translate('xpack.siem.DNS.histogramTitle', {
defaultMessage: 'Top DNS domains bytes count',
});
export const STACK_BY = i18n.translate(
'xpack.siem.components.histogram.stackByOptions.stackByLabel',
{
defaultMessage: 'Stack by',
}
);

View file

@ -4,29 +4,119 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ScaleType } from '@elastic/charts';
import { MatrixOverTimeHistogramData, MatrixOverOrdinalHistogramData } from '../../graphql/types';
import { AuthMatrixDataFields } from '../page/hosts/authentications_over_time/utils';
import { ScaleType, Position } from '@elastic/charts';
import { SetStateAction } from 'react';
import { DocumentNode } from 'graphql';
import {
MatrixOverTimeHistogramData,
MatrixOverOrdinalHistogramData,
NetworkDnsSortField,
PaginationInputPaginated,
} from '../../graphql/types';
import { UpdateDateRange } from '../charts/common';
import { ESQuery } from '../../../common/typed_json';
import { SetQuery } from '../../pages/hosts/navigation/types';
export type MatrixHistogramDataTypes = MatrixOverTimeHistogramData | MatrixOverOrdinalHistogramData;
export type MatrixHistogramMappingTypes = AuthMatrixDataFields;
export interface MatrixHistogramBasicProps<T> {
data: T[];
export type MatrixHistogramMappingTypes = Record<
string,
{ key: string; value: null; color?: string | undefined }
>;
export interface MatrixHistogramOption {
text: string;
value: string;
}
export type GetSubTitle = (count: number) => string;
export interface MatrixHistogramBasicProps {
defaultIndex: string[];
defaultStackByOption: MatrixHistogramOption;
endDate: number;
hideHistogramIfEmpty?: boolean;
id: string;
loading: boolean;
mapping?: MatrixHistogramMappingTypes;
setQuery: SetQuery;
sourceId: string;
startDate: number;
totalCount: number;
stackByOptions: MatrixHistogramOption[];
subtitle?: string | GetSubTitle;
title?: string;
updateDateRange: UpdateDateRange;
}
export interface MatrixHistogramProps<T> extends MatrixHistogramBasicProps<T> {
dataKey?: string;
export interface MatrixHistogramQueryProps {
activePage?: number;
dataKey: string;
endDate: number;
errorMessage: string;
filterQuery?: ESQuery | string | undefined;
limit?: number;
query: DocumentNode;
sort?: NetworkDnsSortField;
stackByField: string;
skip: boolean;
startDate: number;
title: string;
isAlertsHistogram?: boolean;
isAnomaliesHistogram?: boolean;
isAuthenticationsHistogram?: boolean;
isDNSHistogram?: boolean;
isEventsType?: boolean;
isInspected: boolean;
isPtrIncluded?: boolean;
pagination?: PaginationInputPaginated;
}
export interface MatrixHistogramProps extends MatrixHistogramBasicProps {
scaleType?: ScaleType;
subtitle?: string;
title?: string;
yTickFormatter?: (value: number) => string;
showLegend?: boolean;
legendPosition?: Position;
}
export interface HistogramBucket {
key_as_string: string;
key: number;
doc_count: number;
}
export interface GroupBucket {
key: string;
signals: {
buckets: HistogramBucket[];
};
}
export interface HistogramAggregation {
histogramAgg: {
buckets: GroupBucket[];
};
}
export interface SignalsResponse {
took: number;
timeout: boolean;
}
export interface SignalSearchResponse<Hit = {}, Aggregations = {} | undefined>
extends SignalsResponse {
_shards: {
total: number;
successful: number;
skipped: number;
failed: number;
};
aggregations?: Aggregations;
hits: {
total: {
value: number;
relation: string;
};
hits: Hit[];
};
}
export type Return<Hit, Aggs> = [
boolean,
SignalSearchResponse<Hit, Aggs> | null,
React.Dispatch<SetStateAction<string>>
];

View file

@ -3,7 +3,6 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ScaleType, niceTimeFormatter, Position } from '@elastic/charts';
import { get, groupBy, map, toPairs } from 'lodash/fp';
@ -17,6 +16,7 @@ export const getBarchartConfigs = ({
onBrushEnd,
yTickFormatter,
showLegend,
legendPosition,
}: {
from: number;
to: number;
@ -24,6 +24,7 @@ export const getBarchartConfigs = ({
onBrushEnd: UpdateDateRange;
yTickFormatter?: (value: number) => string;
showLegend?: boolean;
legendPosition?: Position;
}) => ({
series: {
xScaleType: scaleType || ScaleType.Time,
@ -39,7 +40,7 @@ export const getBarchartConfigs = ({
tickSize: 8,
},
settings: {
legendPosition: Position.Bottom,
legendPosition: legendPosition || Position.Bottom,
onBrushEnd,
showLegend: showLegend || true,
theme: {
@ -72,18 +73,18 @@ export const formatToChartDataItem = ([key, value]: [
});
export const getCustomChartData = (
data: MatrixHistogramDataTypes[],
data: MatrixHistogramDataTypes[] | null,
mapping?: MatrixHistogramMappingTypes
): ChartSeriesData[] => {
if (!data) return [];
const dataGroupedByEvent = groupBy('g', data);
const dataGroupedEntries = toPairs(dataGroupedByEvent);
const formattedChartData = map(formatToChartDataItem, dataGroupedEntries);
if (mapping)
return map((item: ChartSeriesData) => {
const customColor = get(`${item.key}.color`, mapping);
item.color = customColor;
return item;
const mapItem = get(item.key, mapping);
return { ...item, color: mapItem.color };
}, formattedChartData);
else return formattedChartData;
};

View file

@ -1,30 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import * as i18n from './translation';
import { MatrixHistogram } from '../../../matrix_histogram';
import { MatrixHistogramBasicProps } from '../../../matrix_histogram/types';
import { MatrixOverTimeHistogramData } from '../../../../graphql/types';
export const AlertsOverTimeHistogram = (
props: MatrixHistogramBasicProps<MatrixOverTimeHistogramData>
) => {
const dataKey = 'alertsOverTime';
const { totalCount } = props;
const subtitle = `${i18n.SHOWING}: ${totalCount.toLocaleString()} ${i18n.UNIT(totalCount)}`;
const { ...matrixOverTimeProps } = props;
return (
<MatrixHistogram
title={i18n.ALERTS_COUNT_FREQUENCY_BY_MODULE}
subtitle={subtitle}
dataKey={dataKey}
{...matrixOverTimeProps}
/>
);
};

View file

@ -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;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
export const ALERTS_COUNT_FREQUENCY_BY_MODULE = i18n.translate(
'xpack.siem.alertsOverTime.alertsCountFrequencyByModuleTitle',
{
defaultMessage: 'Alerts count by module',
}
);
export const SHOWING = i18n.translate('xpack.siem.alertsOverTime.showing', {
defaultMessage: 'Showing',
});
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.alertsOverTime.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {alert} other {alerts}}`,
});

View file

@ -1,30 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import * as i18n from './translation';
import { MatrixHistogram } from '../../../matrix_histogram';
import { MatrixHistogramBasicProps } from '../../../matrix_histogram/types';
import { MatrixOverTimeHistogramData } from '../../../../graphql/types';
import { authMatrixDataMappingFields } from './utils';
export const AuthenticationsOverTimeHistogram = (
props: MatrixHistogramBasicProps<MatrixOverTimeHistogramData>
) => {
const dataKey = 'authenticationsOverTime';
const { data, ...matrixOverTimeProps } = props;
return (
<MatrixHistogram
mapping={authMatrixDataMappingFields}
dataKey={dataKey}
data={data}
title={i18n.AUTHENTICATIONS_COUNT}
{...matrixOverTimeProps}
/>
);
};

View file

@ -1,20 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
export const AUTHENTICATIONS_COUNT = i18n.translate(
'xpack.siem.authenticationsOverTime.authenticationCountTitle',
{
defaultMessage: 'Authentications count',
}
);
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.authenticationsOverTime.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {authentication} other {authentications}}`,
});

View file

@ -1,31 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import { ChartSeriesData } from '../../../charts/common';
import { KpiHostsChartColors } from '../kpi_hosts/types';
enum AuthMatrixDataGroup {
authSuccess = 'authentication_success',
authFailure = 'authentication_failure',
}
export interface AuthMatrixDataFields {
[AuthMatrixDataGroup.authSuccess]: ChartSeriesData;
[AuthMatrixDataGroup.authFailure]: ChartSeriesData;
}
export const authMatrixDataMappingFields: AuthMatrixDataFields = {
[AuthMatrixDataGroup.authSuccess]: {
key: AuthMatrixDataGroup.authSuccess,
value: null,
color: KpiHostsChartColors.authSuccess,
},
[AuthMatrixDataGroup.authFailure]: {
key: AuthMatrixDataGroup.authFailure,
value: null,
color: KpiHostsChartColors.authFailure,
},
};

View file

@ -1,30 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import * as i18n from './translation';
import { MatrixHistogram } from '../../../matrix_histogram';
import { MatrixHistogramBasicProps } from '../../../matrix_histogram/types';
import { MatrixOverTimeHistogramData } from '../../../../graphql/types';
export const EventsOverTimeHistogram = (
props: MatrixHistogramBasicProps<MatrixOverTimeHistogramData>
) => {
const dataKey = 'eventsOverTime';
const { totalCount } = props;
const subtitle = `${i18n.SHOWING}: ${totalCount.toLocaleString()} ${i18n.UNIT(totalCount)}`;
const { ...matrixOverTimeProps } = props;
return (
<MatrixHistogram
title={i18n.EVENT_COUNT_FREQUENCY_BY_ACTION}
subtitle={subtitle}
dataKey={dataKey}
{...matrixOverTimeProps}
/>
);
};

View file

@ -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;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
export const EVENT_COUNT_FREQUENCY_BY_ACTION = i18n.translate(
'xpack.siem.eventsOverTime.eventCountFrequencyByActionTitle',
{
defaultMessage: 'Event count by action',
}
);
export const SHOWING = i18n.translate('xpack.siem.eventsOverTime.showing', {
defaultMessage: 'Showing',
});
export const UNIT = (totalCount: number) =>
i18n.translate('xpack.siem.eventsOverTime.unit', {
values: { totalCount },
defaultMessage: `{totalCount, plural, =1 {event} other {events}}`,
});

View file

@ -8,18 +8,14 @@ import { omit } from 'lodash/fp';
import React from 'react';
import { inputsModel } from '../../store';
import { SetQuery } from '../../pages/hosts/navigation/types';
interface OwnProps {
deleteQuery?: ({ id }: { id: string }) => void;
id: string;
loading: boolean;
refetch: inputsModel.Refetch;
setQuery: (params: {
id: string;
inspect: inputsModel.InspectQuery | null;
loading: boolean;
refetch: inputsModel.Refetch;
}) => void;
setQuery: SetQuery;
inspect?: inputsModel.InspectQuery;
}

View file

@ -1,33 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { ScaleType } from '@elastic/charts';
import * as i18n from './translation';
import { MatrixHistogram } from '../../../matrix_histogram';
import { MatrixOverOrdinalHistogramData } from '../../../../graphql/types';
import { MatrixHistogramBasicProps } from '../../../matrix_histogram/types';
import { useFormatBytes } from '../../../formatted_bytes';
export const NetworkDnsHistogram = (
props: MatrixHistogramBasicProps<MatrixOverOrdinalHistogramData>
) => {
const dataKey = 'histogram';
const { ...matrixOverTimeProps } = props;
const formatBytes = useFormatBytes();
return (
<MatrixHistogram
title={i18n.NETWORK_DNS_HISTOGRAM}
dataKey={dataKey}
scaleType={ScaleType.Ordinal}
yTickFormatter={formatBytes}
showLegend={false}
{...matrixOverTimeProps}
/>
);
};

View file

@ -1,37 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import gql from 'graphql-tag';
export const AlertsOverTimeGqlQuery = gql`
query GetAlertsOverTimeQuery(
$sourceId: ID!
$timerange: TimerangeInput!
$defaultIndex: [String!]!
$filterQuery: String
$inspect: Boolean!
) {
source(id: $sourceId) {
id
AlertsHistogram(
timerange: $timerange
filterQuery: $filterQuery
defaultIndex: $defaultIndex
) {
alertsOverTimeByModule {
x
y
g
}
totalCount
inspect @include(if: $inspect) {
dsl
response
}
}
}
}
`;

View file

@ -1,113 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import { getOr } from 'lodash/fp';
import React from 'react';
import { Query } from 'react-apollo';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { DEFAULT_INDEX_KEY } from '../../../../common/constants';
import { inputsModel, State, inputsSelectors, hostsModel } from '../../../store';
import { withKibana, WithKibanaProps } from '../../../lib/kibana';
import { createFilter, getDefaultFetchPolicy } from '../../helpers';
import { QueryTemplate, QueryTemplateProps } from '../../query_template';
import { AlertsOverTimeGqlQuery } from './alerts_over_time.gql_query';
import { MatrixOverTimeHistogramData, GetAlertsOverTimeQuery } from '../../../graphql/types';
const ID = 'alertsOverTimeQuery';
export interface AlertsArgs {
endDate: number;
alertsOverTime: MatrixOverTimeHistogramData[];
id: string;
inspect: inputsModel.InspectQuery;
loading: boolean;
refetch: inputsModel.Refetch;
startDate: number;
totalCount: number;
}
export interface OwnProps extends QueryTemplateProps {
children?: (args: AlertsArgs) => React.ReactNode;
type: hostsModel.HostsType;
}
export interface AlertsOverTimeComponentReduxProps {
isInspected: boolean;
}
type AlertsOverTimeProps = OwnProps & AlertsOverTimeComponentReduxProps & WithKibanaProps;
class AlertsOverTimeComponentQuery extends QueryTemplate<
AlertsOverTimeProps,
GetAlertsOverTimeQuery.Query,
GetAlertsOverTimeQuery.Variables
> {
public render() {
const {
children,
endDate,
filterQuery,
id = ID,
isInspected,
kibana,
sourceId,
startDate,
} = this.props;
return (
<Query<GetAlertsOverTimeQuery.Query, GetAlertsOverTimeQuery.Variables>
query={AlertsOverTimeGqlQuery}
fetchPolicy={getDefaultFetchPolicy()}
notifyOnNetworkStatusChange
variables={{
filterQuery: createFilter(filterQuery),
sourceId,
timerange: {
interval: '12h',
from: startDate!,
to: endDate!,
},
defaultIndex: kibana.services.uiSettings.get<string[]>(DEFAULT_INDEX_KEY),
inspect: isInspected,
}}
>
{({ data, loading, refetch }) => {
const source = getOr({}, `source.AlertsHistogram`, data);
const alertsOverTime = getOr([], `alertsOverTimeByModule`, source);
const totalCount = getOr(-1, 'totalCount', source);
return children!({
endDate: endDate!,
alertsOverTime,
id,
inspect: getOr(null, 'inspect', source),
loading,
refetch,
startDate: startDate!,
totalCount,
});
}}
</Query>
);
}
}
const makeMapStateToProps = () => {
const getQuery = inputsSelectors.globalQueryByIdSelector();
const mapStateToProps = (state: State, { type, id = ID }: OwnProps) => {
const { isInspected } = getQuery(state, id);
return {
isInspected,
};
};
return mapStateToProps;
};
export const AlertsOverTimeQuery = compose<React.ComponentClass<OwnProps>>(
connect(makeMapStateToProps),
withKibana
)(AlertsOverTimeComponentQuery);

View file

@ -1,37 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import gql from 'graphql-tag';
export const AnomaliesOverTimeGqlQuery = gql`
query GetAnomaliesOverTimeQuery(
$sourceId: ID!
$timerange: TimerangeInput!
$defaultIndex: [String!]!
$filterQuery: String
$inspect: Boolean!
) {
source(id: $sourceId) {
id
AnomaliesOverTime(
timerange: $timerange
filterQuery: $filterQuery
defaultIndex: $defaultIndex
) {
anomaliesOverTime {
x
y
g
}
totalCount
inspect @include(if: $inspect) {
dsl
response
}
}
}
}
`;

View file

@ -1,86 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import { getOr } from 'lodash/fp';
import React from 'react';
import { Query } from 'react-apollo';
import { connect } from 'react-redux';
import { State, inputsSelectors } from '../../../store';
import { getDefaultFetchPolicy } from '../../helpers';
import { QueryTemplate } from '../../query_template';
import { AnomaliesOverTimeGqlQuery } from './anomalies_over_time.gql_query';
import { GetAnomaliesOverTimeQuery } from '../../../graphql/types';
import { AnomaliesOverTimeProps, OwnProps } from './types';
const ID = 'anomaliesOverTimeQuery';
class AnomaliesOverTimeComponentQuery extends QueryTemplate<
AnomaliesOverTimeProps,
GetAnomaliesOverTimeQuery.Query,
GetAnomaliesOverTimeQuery.Variables
> {
public render() {
const {
children,
endDate,
filterQuery,
id = ID,
isInspected,
sourceId,
startDate,
} = this.props;
return (
<Query<GetAnomaliesOverTimeQuery.Query, GetAnomaliesOverTimeQuery.Variables>
query={AnomaliesOverTimeGqlQuery}
fetchPolicy={getDefaultFetchPolicy()}
notifyOnNetworkStatusChange
variables={{
filterQuery,
sourceId,
timerange: {
interval: 'day',
from: startDate!,
to: endDate!,
},
defaultIndex: ['.ml-anomalies-*'],
inspect: isInspected,
}}
>
{({ data, loading, refetch }) => {
const source = getOr({}, `source.AnomaliesOverTime`, data);
const anomaliesOverTime = getOr([], `anomaliesOverTime`, source);
const totalCount = getOr(-1, 'totalCount', source);
return children!({
endDate: endDate!,
anomaliesOverTime,
id,
inspect: getOr(null, 'inspect', source),
loading,
refetch,
startDate: startDate!,
totalCount,
});
}}
</Query>
);
}
}
const makeMapStateToProps = () => {
const getQuery = inputsSelectors.globalQueryByIdSelector();
const mapStateToProps = (state: State, { id = ID }: OwnProps) => {
const { isInspected } = getQuery(state, id);
return {
isInspected,
};
};
return mapStateToProps;
};
export const AnomaliesOverTimeQuery = connect(makeMapStateToProps)(AnomaliesOverTimeComponentQuery);

View file

@ -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;
* you may not use this file except in compliance with the Elastic License.
*/
import { QueryTemplateProps } from '../../query_template';
import { inputsModel, hostsModel, networkModel } from '../../../store';
import { MatrixOverTimeHistogramData } from '../../../graphql/types';
export interface AnomaliesArgs {
endDate: number;
anomaliesOverTime: MatrixOverTimeHistogramData[];
id: string;
inspect: inputsModel.InspectQuery;
loading: boolean;
refetch: inputsModel.Refetch;
startDate: number;
totalCount: number;
}
export interface OwnProps extends Omit<QueryTemplateProps, 'filterQuery'> {
filterQuery?: string;
children?: (args: AnomaliesArgs) => React.ReactNode;
type: hostsModel.HostsType | networkModel.NetworkType;
}
export interface AnomaliesOverTimeComponentReduxProps {
isInspected: boolean;
}
export type AnomaliesOverTimeProps = OwnProps & AnomaliesOverTimeComponentReduxProps;

View file

@ -4,35 +4,42 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { useEffect } from 'react';
import { EuiSpacer } from '@elastic/eui';
import * as i18n from './translations';
import { AnomaliesQueryTabBodyProps } from './types';
import { manageQuery } from '../../../components/page/manage_query';
import { AnomaliesOverTimeHistogram } from '../../../components/anomalies_over_time';
import { AnomaliesOverTimeQuery } from '../anomalies_over_time';
import { getAnomaliesFilterQuery } from './utils';
import { useSiemJobs } from '../../../components/ml_popover/hooks/use_siem_jobs';
import { useUiSetting$ } from '../../../lib/kibana';
import { DEFAULT_ANOMALY_SCORE } from '../../../../common/constants';
import { MatrixHistogramContainer } from '../../matrix_histogram';
import { MatrixHistogramOption } from '../../../components/matrix_histogram/types';
import { MatrixHistogramGqlQuery } from '../../matrix_histogram/index.gql_query';
const AnomaliesOverTimeManage = manageQuery(AnomaliesOverTimeHistogram);
const ID = 'anomaliesOverTimeQuery';
const anomaliesStackByOptions: MatrixHistogramOption[] = [
{
text: i18n.ANOMALIES_STACK_BY_JOB_ID,
value: 'job_id',
},
];
export const AnomaliesQueryTabBody = ({
deleteQuery,
endDate,
setQuery,
skip,
startDate,
type,
narrowDateRange,
filterQuery,
anomaliesFilterQuery,
setQuery,
hideHistogramIfEmpty,
updateDateRange = () => {},
AnomaliesTableComponent,
flowTarget,
ip,
}: AnomaliesQueryTabBodyProps) => {
const [siemJobsLoading, siemJobs] = useSiemJobs(true);
const [, siemJobs] = useSiemJobs(true);
const [anomalyScore] = useUiSetting$<number>(DEFAULT_ANOMALY_SCORE);
const mergedFilterQuery = getAnomaliesFilterQuery(
@ -44,39 +51,37 @@ export const AnomaliesQueryTabBody = ({
ip
);
useEffect(() => {
return () => {
if (deleteQuery) {
deleteQuery({ id: ID });
}
};
}, []);
return (
<>
<AnomaliesOverTimeQuery
<MatrixHistogramContainer
isAnomaliesHistogram={true}
dataKey="AnomaliesHistogram"
defaultStackByOption={anomaliesStackByOptions[0]}
deleteQuery={deleteQuery}
endDate={endDate}
errorMessage={i18n.ERROR_FETCHING_ANOMALIES_DATA}
filterQuery={mergedFilterQuery}
hideHistogramIfEmpty={true}
id={ID}
query={MatrixHistogramGqlQuery}
setQuery={setQuery}
skip={skip}
sourceId="default"
stackByOptions={anomaliesStackByOptions}
startDate={startDate}
title={i18n.ANOMALIES_TITLE}
type={type}
>
{({ anomaliesOverTime, loading, id, inspect, refetch, totalCount }) => {
if (hideHistogramIfEmpty && !anomaliesOverTime.length) {
return <div />;
}
return (
<>
<AnomaliesOverTimeManage
data={anomaliesOverTime!}
endDate={endDate}
id={id}
inspect={inspect}
loading={siemJobsLoading || loading}
refetch={refetch}
setQuery={setQuery}
startDate={startDate}
totalCount={totalCount}
updateDateRange={updateDateRange}
/>
<EuiSpacer />
</>
);
}}
</AnomaliesOverTimeQuery>
updateDateRange={updateDateRange}
/>
<EuiSpacer />
<AnomaliesTableComponent
startDate={startDate}
endDate={endDate}

View file

@ -0,0 +1,25 @@
/*
* 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 ANOMALIES_STACK_BY_JOB_ID = i18n.translate(
'xpack.siem.containers.anomalies.stackByJobId',
{
defaultMessage: 'job',
}
);
export const ANOMALIES_TITLE = i18n.translate('xpack.siem.containers.anomalies.title', {
defaultMessage: 'Anomalies',
});
export const ERROR_FETCHING_ANOMALIES_DATA = i18n.translate(
'xpack.siem.containers.anomalies.errorFetchingAnomaliesData',
{
defaultMessage: 'Failed to query anomalies data',
}
);

View file

@ -20,15 +20,16 @@ interface QueryTabBodyProps {
}
export type AnomaliesQueryTabBodyProps = QueryTabBodyProps & {
startDate: number;
endDate: number;
skip: boolean;
setQuery: SetQuery;
narrowDateRange: NarrowDateRange;
updateDateRange?: UpdateDateRange;
anomaliesFilterQuery?: object;
AnomaliesTableComponent: typeof AnomaliesHostTable | typeof AnomaliesNetworkTable;
deleteQuery?: ({ id }: { id: string }) => void;
endDate: number;
flowTarget?: FlowTarget;
narrowDateRange: NarrowDateRange;
setQuery: SetQuery;
startDate: number;
skip: boolean;
updateDateRange?: UpdateDateRange;
hideHistogramIfEmpty?: boolean;
ip?: string;
flowTarget?: FlowTarget;
AnomaliesTableComponent: typeof AnomaliesHostTable | typeof AnomaliesNetworkTable;
};

View file

@ -1,37 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import gql from 'graphql-tag';
export const AuthenticationsOverTimeGqlQuery = gql`
query GetAuthenticationsOverTimeQuery(
$sourceId: ID!
$timerange: TimerangeInput!
$defaultIndex: [String!]!
$filterQuery: String
$inspect: Boolean!
) {
source(id: $sourceId) {
id
AuthenticationsOverTime(
timerange: $timerange
filterQuery: $filterQuery
defaultIndex: $defaultIndex
) {
authenticationsOverTime {
x
y
g
}
totalCount
inspect @include(if: $inspect) {
dsl
response
}
}
}
}
`;

View file

@ -1,118 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import { getOr } from 'lodash/fp';
import React from 'react';
import { Query } from 'react-apollo';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { DEFAULT_INDEX_KEY } from '../../../../common/constants';
import { inputsModel, State, inputsSelectors, hostsModel } from '../../../store';
import { withKibana, WithKibanaProps } from '../../../lib/kibana';
import { createFilter, getDefaultFetchPolicy } from '../../helpers';
import { QueryTemplate, QueryTemplateProps } from '../../query_template';
import { AuthenticationsOverTimeGqlQuery } from './authentications_over_time.gql_query';
import {
GetAuthenticationsOverTimeQuery,
MatrixOverTimeHistogramData,
} from '../../../graphql/types';
const ID = 'authenticationsOverTimeQuery';
export interface AuthenticationsArgs {
endDate: number;
authenticationsOverTime: MatrixOverTimeHistogramData[];
id: string;
inspect: inputsModel.InspectQuery;
loading: boolean;
refetch: inputsModel.Refetch;
startDate: number;
totalCount: number;
}
export interface OwnProps extends QueryTemplateProps {
children?: (args: AuthenticationsArgs) => React.ReactNode;
type: hostsModel.HostsType;
}
export interface AuthenticationsOverTimeComponentReduxProps {
isInspected: boolean;
}
type AuthenticationsOverTimeProps = OwnProps &
AuthenticationsOverTimeComponentReduxProps &
WithKibanaProps;
class AuthenticationsOverTimeComponentQuery extends QueryTemplate<
AuthenticationsOverTimeProps,
GetAuthenticationsOverTimeQuery.Query,
GetAuthenticationsOverTimeQuery.Variables
> {
public render() {
const {
children,
filterQuery,
id = ID,
isInspected,
kibana,
sourceId,
startDate,
endDate,
} = this.props;
return (
<Query<GetAuthenticationsOverTimeQuery.Query, GetAuthenticationsOverTimeQuery.Variables>
query={AuthenticationsOverTimeGqlQuery}
fetchPolicy={getDefaultFetchPolicy()}
notifyOnNetworkStatusChange
variables={{
filterQuery: createFilter(filterQuery),
sourceId,
timerange: {
interval: '12h',
from: startDate!,
to: endDate!,
},
defaultIndex: kibana.services.uiSettings.get<string[]>(DEFAULT_INDEX_KEY),
inspect: isInspected,
}}
>
{({ data, loading, refetch }) => {
const source = getOr({}, `source.AuthenticationsOverTime`, data);
const authenticationsOverTime = getOr([], `authenticationsOverTime`, source);
const totalCount = getOr(-1, 'totalCount', source);
return children!({
endDate: endDate!,
authenticationsOverTime,
id,
inspect: getOr(null, 'inspect', source),
loading,
refetch,
startDate: startDate!,
totalCount,
});
}}
</Query>
);
}
}
const makeMapStateToProps = () => {
const getQuery = inputsSelectors.globalQueryByIdSelector();
const mapStateToProps = (state: State, { type, id = ID }: OwnProps) => {
const { isInspected } = getQuery(state, id);
return {
isInspected,
};
};
return mapStateToProps;
};
export const AuthenticationsOverTimeQuery = compose<React.ComponentClass<OwnProps>>(
connect(makeMapStateToProps),
withKibana
)(AuthenticationsOverTimeComponentQuery);

View file

@ -1,37 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import gql from 'graphql-tag';
export const EventsOverTimeGqlQuery = gql`
query GetEventsOverTimeQuery(
$sourceId: ID!
$timerange: TimerangeInput!
$defaultIndex: [String!]!
$filterQuery: String
$inspect: Boolean!
) {
source(id: $sourceId) {
id
EventsOverTime(
timerange: $timerange
filterQuery: $filterQuery
defaultIndex: $defaultIndex
) {
eventsOverTime {
x
y
g
}
totalCount
inspect @include(if: $inspect) {
dsl
response
}
}
}
}
`;

View file

@ -1,113 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import { getOr } from 'lodash/fp';
import React from 'react';
import { Query } from 'react-apollo';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { DEFAULT_INDEX_KEY } from '../../../../common/constants';
import { inputsModel, State, inputsSelectors, hostsModel } from '../../../store';
import { createFilter, getDefaultFetchPolicy } from '../../helpers';
import { QueryTemplate, QueryTemplateProps } from '../../query_template';
import { withKibana, WithKibanaProps } from '../../../lib/kibana';
import { EventsOverTimeGqlQuery } from './events_over_time.gql_query';
import { GetEventsOverTimeQuery, MatrixOverTimeHistogramData } from '../../../graphql/types';
const ID = 'eventsOverTimeQuery';
export interface EventsArgs {
endDate: number;
eventsOverTime: MatrixOverTimeHistogramData[];
id: string;
inspect: inputsModel.InspectQuery;
loading: boolean;
refetch: inputsModel.Refetch;
startDate: number;
totalCount: number;
}
export interface OwnProps extends QueryTemplateProps {
children?: (args: EventsArgs) => React.ReactNode;
type: hostsModel.HostsType;
}
export interface EventsOverTimeComponentReduxProps {
isInspected: boolean;
}
type EventsOverTimeProps = OwnProps & EventsOverTimeComponentReduxProps & WithKibanaProps;
class EventsOverTimeComponentQuery extends QueryTemplate<
EventsOverTimeProps,
GetEventsOverTimeQuery.Query,
GetEventsOverTimeQuery.Variables
> {
public render() {
const {
children,
endDate,
filterQuery,
id = ID,
isInspected,
kibana,
sourceId,
startDate,
} = this.props;
return (
<Query<GetEventsOverTimeQuery.Query, GetEventsOverTimeQuery.Variables>
query={EventsOverTimeGqlQuery}
fetchPolicy={getDefaultFetchPolicy()}
notifyOnNetworkStatusChange
variables={{
filterQuery: createFilter(filterQuery),
sourceId,
timerange: {
interval: '12h',
from: startDate!,
to: endDate!,
},
defaultIndex: kibana.services.uiSettings.get<string[]>(DEFAULT_INDEX_KEY),
inspect: isInspected,
}}
>
{({ data, loading, refetch }) => {
const source = getOr({}, `source.EventsOverTime`, data);
const eventsOverTime = getOr([], `eventsOverTime`, source);
const totalCount = getOr(-1, 'totalCount', source);
return children!({
endDate: endDate!,
eventsOverTime,
id,
inspect: getOr(null, 'inspect', source),
loading,
refetch,
startDate: startDate!,
totalCount,
});
}}
</Query>
);
}
}
const makeMapStateToProps = () => {
const getQuery = inputsSelectors.globalQueryByIdSelector();
const mapStateToProps = (state: State, { type, id = ID }: OwnProps) => {
const { isInspected } = getQuery(state, id);
return {
isInspected,
};
};
return mapStateToProps;
};
export const EventsOverTimeQuery = compose<React.ComponentClass<OwnProps>>(
connect(makeMapStateToProps),
withKibana
)(EventsOverTimeComponentQuery);

View file

@ -0,0 +1,94 @@
/*
* 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 gql from 'graphql-tag';
export const MatrixHistogramGqlQuery = gql`
query GetMatrixHistogramQuery(
$isAlertsHistogram: Boolean!
$isAnomaliesHistogram: Boolean!
$isAuthenticationsHistogram: Boolean!
$defaultIndex: [String!]!
$isEventsType: Boolean!
$filterQuery: String
$inspect: Boolean!
$sourceId: ID!
$stackByField: String
$timerange: TimerangeInput!
) {
source(id: $sourceId) {
id
AlertsHistogram(
timerange: $timerange
filterQuery: $filterQuery
defaultIndex: $defaultIndex
stackByField: $stackByField
) @include(if: $isAlertsHistogram) {
matrixHistogramData {
x
y
g
}
totalCount
inspect @include(if: $inspect) {
dsl
response
}
}
AnomaliesHistogram(
timerange: $timerange
filterQuery: $filterQuery
defaultIndex: $defaultIndex
stackByField: $stackByField
) @include(if: $isAnomaliesHistogram) {
matrixHistogramData {
x
y
g
}
totalCount
inspect @include(if: $inspect) {
dsl
response
}
}
AuthenticationsHistogram(
timerange: $timerange
filterQuery: $filterQuery
defaultIndex: $defaultIndex
stackByField: $stackByField
) @include(if: $isAuthenticationsHistogram) {
matrixHistogramData {
x
y
g
}
totalCount
inspect @include(if: $inspect) {
dsl
response
}
}
EventsHistogram(
timerange: $timerange
filterQuery: $filterQuery
defaultIndex: $defaultIndex
stackByField: $stackByField
) @include(if: $isEventsType) {
matrixHistogramData {
x
y
g
}
totalCount
inspect @include(if: $inspect) {
dsl
response
}
}
}
}
`;

View file

@ -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 React from 'react';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { State, inputsSelectors, hostsModel, networkModel } from '../../store';
import { QueryTemplateProps } from '../query_template';
import { Maybe } from '../../graphql/types';
import { MatrixHistogram } from '../../components/matrix_histogram';
import {
MatrixHistogramOption,
MatrixHistogramMappingTypes,
GetSubTitle,
} from '../../components/matrix_histogram/types';
import { UpdateDateRange } from '../../components/charts/common';
import { SetQuery } from '../../pages/hosts/navigation/types';
export interface OwnProps extends QueryTemplateProps {
isAlertsHistogram?: boolean;
isAnomaliesHistogram?: boolean;
isAuthenticationsHistogram?: boolean;
dataKey: string | string[];
defaultStackByOption: MatrixHistogramOption;
deleteQuery?: ({ id }: { id: string }) => void;
isEventsType?: boolean;
errorMessage: string;
hideHistogramIfEmpty?: boolean;
id: string;
mapping?: MatrixHistogramMappingTypes;
query: Maybe<string>;
setQuery: SetQuery;
sourceId: string;
stackByOptions: MatrixHistogramOption[];
subtitle?: string | GetSubTitle;
title: string;
type: hostsModel.HostsType | networkModel.NetworkType;
updateDateRange: UpdateDateRange;
}
const makeMapStateToProps = () => {
const getQuery = inputsSelectors.globalQueryByIdSelector();
const mapStateToProps = (state: State, { type, id }: OwnProps) => {
const { isInspected } = getQuery(state, id);
return {
isInspected,
};
};
return mapStateToProps;
};
export const MatrixHistogramContainer = compose<React.ComponentClass<OwnProps>>(
connect(makeMapStateToProps)
)(MatrixHistogram);

View file

@ -0,0 +1,158 @@
/*
* 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 { getOr } from 'lodash/fp';
import { useEffect, useState } from 'react';
import {
MatrixHistogramDataTypes,
MatrixHistogramQueryProps,
} from '../../components/matrix_histogram/types';
import { DEFAULT_INDEX_KEY } from '../../../common/constants';
import { useStateToaster } from '../../components/toasters';
import { errorToToaster } from '../../components/ml/api/error_to_toaster';
import { useUiSetting$ } from '../../lib/kibana';
import { createFilter } from '../helpers';
import { useApolloClient } from '../../utils/apollo_context';
import { inputsModel } from '../../store';
import { GetMatrixHistogramQuery, GetNetworkDnsQuery } from '../../graphql/types';
export const useQuery = <Hit, Aggs, TCache = object>({
dataKey,
endDate,
errorMessage,
filterQuery,
isAlertsHistogram = false,
isAnomaliesHistogram = false,
isAuthenticationsHistogram = false,
isEventsType = false,
isDNSHistogram,
isPtrIncluded,
isInspected,
query,
stackByField,
startDate,
sort,
pagination,
}: MatrixHistogramQueryProps) => {
const [defaultIndex] = useUiSetting$<string[]>(DEFAULT_INDEX_KEY);
const [, dispatchToaster] = useStateToaster();
const [refetch, setRefetch] = useState<inputsModel.Refetch>();
const [loading, setLoading] = useState<boolean>(false);
const [data, setData] = useState<MatrixHistogramDataTypes[] | null>(null);
const [inspect, setInspect] = useState<inputsModel.InspectQuery | null>(null);
const [totalCount, setTotalCount] = useState(-1);
const apolloClient = useApolloClient();
const isDNSQuery = (
variable: Pick<
MatrixHistogramQueryProps,
'isDNSHistogram' | 'isPtrIncluded' | 'sort' | 'pagination'
>
): variable is GetNetworkDnsQuery.Variables => {
return (
!!isDNSHistogram &&
variable.isDNSHistogram !== undefined &&
variable.isPtrIncluded !== undefined &&
variable.sort !== undefined &&
variable.pagination !== undefined
);
};
const basicVariables = {
filterQuery: createFilter(filterQuery),
sourceId: 'default',
timerange: {
interval: '12h',
from: startDate!,
to: endDate!,
},
defaultIndex,
inspect: isInspected,
stackByField,
};
const dnsVariables = {
...basicVariables,
isDNSHistogram,
isPtrIncluded,
sort,
pagination,
};
const matrixHistogramVariables: GetMatrixHistogramQuery.Variables = {
...basicVariables,
isAlertsHistogram,
isAnomaliesHistogram,
isAuthenticationsHistogram,
isEventsType,
};
useEffect(() => {
let isSubscribed = true;
const abortCtrl = new AbortController();
const abortSignal = abortCtrl.signal;
async function fetchData() {
if (!apolloClient || (pagination != null && pagination.querySize < 0)) return null;
setLoading(true);
return apolloClient
.query<
GetMatrixHistogramQuery.Query | GetNetworkDnsQuery.Query,
GetMatrixHistogramQuery.Variables | GetNetworkDnsQuery.Variables
>({
query,
fetchPolicy: 'cache-first',
variables: isDNSQuery(dnsVariables) ? dnsVariables : matrixHistogramVariables,
context: {
fetchOptions: {
abortSignal,
},
},
})
.then(
result => {
if (isSubscribed) {
const isDataKeyAnArray = Array.isArray(dataKey);
const rootDataKey = isDataKeyAnArray ? dataKey[0] : `${dataKey}`;
const histogramDataKey = isDataKeyAnArray ? dataKey[1] : `matrixHistogramData`;
const source = getOr({}, `data.source.${rootDataKey}`, result);
setData(getOr([], histogramDataKey, source));
setTotalCount(getOr(-1, 'totalCount', source));
setInspect(getOr(null, 'inspect', source));
setLoading(false);
}
},
error => {
if (isSubscribed) {
setData(null);
setTotalCount(-1);
setInspect(null);
errorToToaster({ title: errorMessage, error, dispatchToaster });
setLoading(false);
}
}
);
}
setRefetch(() => {
fetchData();
});
fetchData();
return () => {
isSubscribed = false;
abortCtrl.abort();
};
}, [
defaultIndex,
query,
filterQuery,
isInspected,
isDNSHistogram,
stackByField,
sort,
isPtrIncluded,
pagination,
startDate,
endDate,
]);
return { data, loading, inspect, totalCount, refetch };
};

View file

@ -8,14 +8,16 @@ import gql from 'graphql-tag';
export const networkDnsQuery = gql`
query GetNetworkDnsQuery(
$sourceId: ID!
$sort: NetworkDnsSortField!
$isPtrIncluded: Boolean!
$timerange: TimerangeInput!
$pagination: PaginationInputPaginated!
$filterQuery: String
$defaultIndex: [String!]!
$filterQuery: String
$inspect: Boolean!
$isDNSHistogram: Boolean!
$isPtrIncluded: Boolean!
$pagination: PaginationInputPaginated!
$sort: NetworkDnsSortField!
$sourceId: ID!
$stackByField: String
$timerange: TimerangeInput!
) {
source(id: $sourceId) {
id
@ -26,9 +28,10 @@ export const networkDnsQuery = gql`
pagination: $pagination
filterQuery: $filterQuery
defaultIndex: $defaultIndex
stackByField: $stackByField
) {
totalCount
edges {
edges @skip(if: $isDNSHistogram) {
node {
_id
dnsBytesIn
@ -41,7 +44,7 @@ export const networkDnsQuery = gql`
value
}
}
pageInfo {
pageInfo @skip(if: $isDNSHistogram) {
activePage
fakeTotalCount
showMorePagesIndicator
@ -50,7 +53,7 @@ export const networkDnsQuery = gql`
dsl
response
}
histogram {
histogram @include(if: $isDNSHistogram) {
x
y
g

View file

@ -10,6 +10,8 @@ import { Query } from 'react-apollo';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { DocumentNode } from 'graphql';
import { ScaleType } from '@elastic/charts';
import { DEFAULT_INDEX_KEY } from '../../../common/constants';
import {
GetNetworkDnsQuery,
@ -24,10 +26,14 @@ import { generateTablePaginationOptions } from '../../components/paginated_table
import { createFilter, getDefaultFetchPolicy } from '../helpers';
import { QueryTemplatePaginated, QueryTemplatePaginatedProps } from '../query_template_paginated';
import { networkDnsQuery } from './index.gql_query';
import { DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from '../../store/constants';
import { DEFAULT_TABLE_ACTIVE_PAGE } from '../../store/constants';
import { MatrixHistogram } from '../../components/matrix_histogram';
import { MatrixHistogramOption, GetSubTitle } from '../../components/matrix_histogram/types';
import { UpdateDateRange } from '../../components/charts/common';
import { SetQuery } from '../../pages/hosts/navigation/types';
const ID = 'networkDnsQuery';
const HISTOGRAM_ID = 'networkDnsHistogramQuery';
export const HISTOGRAM_ID = 'networkDnsHistogramQuery';
export interface NetworkDnsArgs {
id: string;
inspect: inputsModel.InspectQuery;
@ -37,6 +43,7 @@ export interface NetworkDnsArgs {
networkDns: NetworkDnsEdges[];
pageInfo: PageInfoPaginated;
refetch: inputsModel.Refetch;
stackByField?: string;
totalCount: number;
histogram: MatrixOverOrdinalHistogramData[];
}
@ -46,6 +53,24 @@ export interface OwnProps extends QueryTemplatePaginatedProps {
type: networkModel.NetworkType;
}
interface DnsHistogramOwnProps extends QueryTemplatePaginatedProps {
dataKey: string | string[];
defaultStackByOption: MatrixHistogramOption;
errorMessage: string;
isDNSHistogram?: boolean;
limit: number;
query: DocumentNode;
scaleType: ScaleType;
setQuery: SetQuery;
showLegend?: boolean;
stackByOptions: MatrixHistogramOption[];
subtitle?: string | GetSubTitle;
title: string;
type: networkModel.NetworkType;
updateDateRange: UpdateDateRange;
yTickFormatter?: (value: number) => string;
}
export interface NetworkDnsComponentReduxProps {
activePage: number;
sort: NetworkDnsSortField;
@ -80,6 +105,7 @@ export class NetworkDnsComponentQuery extends QueryTemplatePaginated<
const variables: GetNetworkDnsQuery.Variables = {
defaultIndex: kibana.services.uiSettings.get<string[]>(DEFAULT_INDEX_KEY),
filterQuery: createFilter(filterQuery),
isDNSHistogram: false,
inspect: isInspected,
isPtrIncluded,
pagination: generateTablePaginationOptions(activePage, limit),
@ -160,12 +186,12 @@ const makeMapStateToProps = () => {
const makeMapHistogramStateToProps = () => {
const getNetworkDnsSelector = networkSelectors.dnsSelector();
const getQuery = inputsSelectors.globalQueryByIdSelector();
const mapStateToProps = (state: State, { id = HISTOGRAM_ID }: OwnProps) => {
const mapStateToProps = (state: State, { id = HISTOGRAM_ID, limit }: DnsHistogramOwnProps) => {
const { isInspected } = getQuery(state, id);
return {
...getNetworkDnsSelector(state),
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
limit: DEFAULT_TABLE_LIMIT,
limit,
isInspected,
id,
};
@ -179,7 +205,7 @@ export const NetworkDnsQuery = compose<React.ComponentClass<OwnProps>>(
withKibana
)(NetworkDnsComponentQuery);
export const NetworkDnsHistogramQuery = compose<React.ComponentClass<OwnProps>>(
export const NetworkDnsHistogramQuery = compose<React.ComponentClass<DnsHistogramOwnProps>>(
connect(makeMapHistogramStateToProps),
withKibana
)(NetworkDnsComponentQuery);
)(MatrixHistogram);

View file

@ -703,6 +703,12 @@
"ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null }
},
"defaultValue": null
},
{
"name": "stackByField",
"description": "",
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
"defaultValue": null
}
],
"type": {
@ -714,7 +720,7 @@
"deprecationReason": null
},
{
"name": "AnomaliesOverTime",
"name": "AnomaliesHistogram",
"description": "",
"args": [
{
@ -750,6 +756,12 @@
}
},
"defaultValue": null
},
{
"name": "stackByField",
"description": "",
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
"defaultValue": null
}
],
"type": {
@ -822,7 +834,7 @@
"deprecationReason": null
},
{
"name": "AuthenticationsOverTime",
"name": "AuthenticationsHistogram",
"description": "",
"args": [
{
@ -858,6 +870,12 @@
}
},
"defaultValue": null
},
{
"name": "stackByField",
"description": "",
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
"defaultValue": null
}
],
"type": {
@ -1058,7 +1076,7 @@
"deprecationReason": null
},
{
"name": "EventsOverTime",
"name": "EventsHistogram",
"description": "",
"args": [
{
@ -1094,6 +1112,12 @@
}
},
"defaultValue": null
},
{
"name": "stackByField",
"description": "",
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
"defaultValue": null
}
],
"type": {
@ -1834,6 +1858,12 @@
},
"defaultValue": null
},
{
"name": "stackByField",
"description": "",
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
"defaultValue": null
},
{
"name": "timerange",
"description": "",
@ -2599,7 +2629,7 @@
"deprecationReason": null
},
{
"name": "alertsOverTimeByModule",
"name": "matrixHistogramData",
"description": "",
"args": [],
"type": {
@ -2752,7 +2782,7 @@
"deprecationReason": null
},
{
"name": "anomaliesOverTime",
"name": "matrixHistogramData",
"description": "",
"args": [],
"type": {
@ -3516,7 +3546,7 @@
"deprecationReason": null
},
{
"name": "authenticationsOverTime",
"name": "matrixHistogramData",
"description": "",
"args": [],
"type": {
@ -6235,7 +6265,7 @@
"deprecationReason": null
},
{
"name": "eventsOverTime",
"name": "matrixHistogramData",
"description": "",
"args": [],
"type": {

View file

@ -459,11 +459,11 @@ export interface Source {
AlertsHistogram: AlertsOverTimeData;
AnomaliesOverTime: AnomaliesOverTimeData;
AnomaliesHistogram: AnomaliesOverTimeData;
/** Gets Authentication success and failures based on a timerange */
Authentications: AuthenticationsData;
AuthenticationsOverTime: AuthenticationsOverTimeData;
AuthenticationsHistogram: AuthenticationsOverTimeData;
Timeline: TimelineData;
@ -471,7 +471,7 @@ export interface Source {
LastEventTime: LastEventTimeData;
EventsOverTime: EventsOverTimeData;
EventsHistogram: EventsOverTimeData;
/** Gets Hosts based on timerange and specified criteria, or all events in the timerange if no criteria is specified */
Hosts: HostsData;
@ -563,7 +563,7 @@ export interface IndexField {
export interface AlertsOverTimeData {
inspect?: Maybe<Inspect>;
alertsOverTimeByModule: MatrixOverTimeHistogramData[];
matrixHistogramData: MatrixOverTimeHistogramData[];
totalCount: number;
}
@ -585,7 +585,7 @@ export interface MatrixOverTimeHistogramData {
export interface AnomaliesOverTimeData {
inspect?: Maybe<Inspect>;
anomaliesOverTime: MatrixOverTimeHistogramData[];
matrixHistogramData: MatrixOverTimeHistogramData[];
totalCount: number;
}
@ -727,7 +727,7 @@ export interface PageInfoPaginated {
export interface AuthenticationsOverTimeData {
inspect?: Maybe<Inspect>;
authenticationsOverTime: MatrixOverTimeHistogramData[];
matrixHistogramData: MatrixOverTimeHistogramData[];
totalCount: number;
}
@ -1311,7 +1311,7 @@ export interface LastEventTimeData {
export interface EventsOverTimeData {
inspect?: Maybe<Inspect>;
eventsOverTime: MatrixOverTimeHistogramData[];
matrixHistogramData: MatrixOverTimeHistogramData[];
totalCount: number;
}
@ -2153,13 +2153,17 @@ export interface AlertsHistogramSourceArgs {
defaultIndex: string[];
timerange: TimerangeInput;
stackByField?: Maybe<string>;
}
export interface AnomaliesOverTimeSourceArgs {
export interface AnomaliesHistogramSourceArgs {
timerange: TimerangeInput;
filterQuery?: Maybe<string>;
defaultIndex: string[];
stackByField?: Maybe<string>;
}
export interface AuthenticationsSourceArgs {
timerange: TimerangeInput;
@ -2170,12 +2174,14 @@ export interface AuthenticationsSourceArgs {
defaultIndex: string[];
}
export interface AuthenticationsOverTimeSourceArgs {
export interface AuthenticationsHistogramSourceArgs {
timerange: TimerangeInput;
filterQuery?: Maybe<string>;
defaultIndex: string[];
stackByField?: Maybe<string>;
}
export interface TimelineSourceArgs {
pagination: PaginationInput;
@ -2206,12 +2212,14 @@ export interface LastEventTimeSourceArgs {
defaultIndex: string[];
}
export interface EventsOverTimeSourceArgs {
export interface EventsHistogramSourceArgs {
timerange: TimerangeInput;
filterQuery?: Maybe<string>;
defaultIndex: string[];
stackByField?: Maybe<string>;
}
export interface HostsSourceArgs {
id?: Maybe<string>;
@ -2340,6 +2348,8 @@ export interface NetworkDnsSourceArgs {
sort: NetworkDnsSortField;
stackByField?: Maybe<string>;
timerange: TimerangeInput;
defaultIndex: string[];
@ -2455,162 +2465,6 @@ export interface DeleteTimelineMutationArgs {
// Documents
// ====================================================
export namespace GetAlertsOverTimeQuery {
export type Variables = {
sourceId: string;
timerange: TimerangeInput;
defaultIndex: string[];
filterQuery?: Maybe<string>;
inspect: boolean;
};
export type Query = {
__typename?: 'Query';
source: Source;
};
export type Source = {
__typename?: 'Source';
id: string;
AlertsHistogram: AlertsHistogram;
};
export type AlertsHistogram = {
__typename?: 'AlertsOverTimeData';
alertsOverTimeByModule: AlertsOverTimeByModule[];
totalCount: number;
inspect: Maybe<Inspect>;
};
export type AlertsOverTimeByModule = {
__typename?: 'MatrixOverTimeHistogramData';
x: number;
y: number;
g: string;
};
export type Inspect = {
__typename?: 'Inspect';
dsl: string[];
response: string[];
};
}
export namespace GetAnomaliesOverTimeQuery {
export type Variables = {
sourceId: string;
timerange: TimerangeInput;
defaultIndex: string[];
filterQuery?: Maybe<string>;
inspect: boolean;
};
export type Query = {
__typename?: 'Query';
source: Source;
};
export type Source = {
__typename?: 'Source';
id: string;
AnomaliesOverTime: AnomaliesOverTime;
};
export type AnomaliesOverTime = {
__typename?: 'AnomaliesOverTimeData';
anomaliesOverTime: _AnomaliesOverTime[];
totalCount: number;
inspect: Maybe<Inspect>;
};
export type _AnomaliesOverTime = {
__typename?: 'MatrixOverTimeHistogramData';
x: number;
y: number;
g: string;
};
export type Inspect = {
__typename?: 'Inspect';
dsl: string[];
response: string[];
};
}
export namespace GetAuthenticationsOverTimeQuery {
export type Variables = {
sourceId: string;
timerange: TimerangeInput;
defaultIndex: string[];
filterQuery?: Maybe<string>;
inspect: boolean;
};
export type Query = {
__typename?: 'Query';
source: Source;
};
export type Source = {
__typename?: 'Source';
id: string;
AuthenticationsOverTime: AuthenticationsOverTime;
};
export type AuthenticationsOverTime = {
__typename?: 'AuthenticationsOverTimeData';
authenticationsOverTime: _AuthenticationsOverTime[];
totalCount: number;
inspect: Maybe<Inspect>;
};
export type _AuthenticationsOverTime = {
__typename?: 'MatrixOverTimeHistogramData';
x: number;
y: number;
g: string;
};
export type Inspect = {
__typename?: 'Inspect';
dsl: string[];
response: string[];
};
}
export namespace GetAuthenticationsQuery {
export type Variables = {
sourceId: string;
@ -2750,58 +2604,6 @@ export namespace GetAuthenticationsQuery {
};
}
export namespace GetEventsOverTimeQuery {
export type Variables = {
sourceId: string;
timerange: TimerangeInput;
defaultIndex: string[];
filterQuery?: Maybe<string>;
inspect: boolean;
};
export type Query = {
__typename?: 'Query';
source: Source;
};
export type Source = {
__typename?: 'Source';
id: string;
EventsOverTime: EventsOverTime;
};
export type EventsOverTime = {
__typename?: 'EventsOverTimeData';
eventsOverTime: _EventsOverTime[];
totalCount: number;
inspect: Maybe<Inspect>;
};
export type _EventsOverTime = {
__typename?: 'MatrixOverTimeHistogramData';
x: number;
y: number;
g: string;
};
export type Inspect = {
__typename?: 'Inspect';
dsl: string[];
response: string[];
};
}
export namespace GetLastEventTimeQuery {
export type Variables = {
sourceId: string;
@ -3423,16 +3225,165 @@ export namespace GetKpiNetworkQuery {
};
}
export namespace GetMatrixHistogramQuery {
export type Variables = {
isAlertsHistogram: boolean;
isAnomaliesHistogram: boolean;
isAuthenticationsHistogram: boolean;
defaultIndex: string[];
isEventsType: boolean;
filterQuery?: Maybe<string>;
inspect: boolean;
sourceId: string;
stackByField?: Maybe<string>;
timerange: TimerangeInput;
};
export type Query = {
__typename?: 'Query';
source: Source;
};
export type Source = {
__typename?: 'Source';
id: string;
AlertsHistogram: AlertsHistogram;
AnomaliesHistogram: AnomaliesHistogram;
AuthenticationsHistogram: AuthenticationsHistogram;
EventsHistogram: EventsHistogram;
};
export type AlertsHistogram = {
__typename?: 'AlertsOverTimeData';
matrixHistogramData: MatrixHistogramData[];
totalCount: number;
inspect: Maybe<Inspect>;
};
export type MatrixHistogramData = {
__typename?: 'MatrixOverTimeHistogramData';
x: number;
y: number;
g: string;
};
export type Inspect = {
__typename?: 'Inspect';
dsl: string[];
response: string[];
};
export type AnomaliesHistogram = {
__typename?: 'AnomaliesOverTimeData';
matrixHistogramData: _MatrixHistogramData[];
totalCount: number;
inspect: Maybe<_Inspect>;
};
export type _MatrixHistogramData = {
__typename?: 'MatrixOverTimeHistogramData';
x: number;
y: number;
g: string;
};
export type _Inspect = {
__typename?: 'Inspect';
dsl: string[];
response: string[];
};
export type AuthenticationsHistogram = {
__typename?: 'AuthenticationsOverTimeData';
matrixHistogramData: __MatrixHistogramData[];
totalCount: number;
inspect: Maybe<__Inspect>;
};
export type __MatrixHistogramData = {
__typename?: 'MatrixOverTimeHistogramData';
x: number;
y: number;
g: string;
};
export type __Inspect = {
__typename?: 'Inspect';
dsl: string[];
response: string[];
};
export type EventsHistogram = {
__typename?: 'EventsOverTimeData';
matrixHistogramData: ___MatrixHistogramData[];
totalCount: number;
inspect: Maybe<___Inspect>;
};
export type ___MatrixHistogramData = {
__typename?: 'MatrixOverTimeHistogramData';
x: number;
y: number;
g: string;
};
export type ___Inspect = {
__typename?: 'Inspect';
dsl: string[];
response: string[];
};
}
export namespace GetNetworkDnsQuery {
export type Variables = {
sourceId: string;
sort: NetworkDnsSortField;
isPtrIncluded: boolean;
timerange: TimerangeInput;
pagination: PaginationInputPaginated;
filterQuery?: Maybe<string>;
defaultIndex: string[];
filterQuery?: Maybe<string>;
inspect: boolean;
isDNSHistogram: boolean;
isPtrIncluded: boolean;
pagination: PaginationInputPaginated;
sort: NetworkDnsSortField;
sourceId: string;
stackByField?: Maybe<string>;
timerange: TimerangeInput;
};
export type Query = {

View file

@ -5,18 +5,48 @@
*/
import { getOr } from 'lodash/fp';
import React from 'react';
import React, { useEffect } from 'react';
import { EuiSpacer } from '@elastic/eui';
import { AuthenticationTable } from '../../../components/page/hosts/authentications_table';
import { manageQuery } from '../../../components/page/manage_query';
import { AuthenticationsOverTimeHistogram } from '../../../components/page/hosts/authentications_over_time';
import { AuthenticationsOverTimeQuery } from '../../../containers/authentications/authentications_over_time';
import { AuthenticationsQuery } from '../../../containers/authentications';
import { HostsComponentsQueryProps } from './types';
import { hostsModel } from '../../../store/hosts';
import {
MatrixHistogramOption,
MatrixHistogramMappingTypes,
} from '../../../components/matrix_histogram/types';
import { MatrixHistogramContainer } from '../../../containers/matrix_histogram';
import { KpiHostsChartColors } from '../../../components/page/hosts/kpi_hosts/types';
import { MatrixHistogramGqlQuery } from '../../../containers/matrix_histogram/index.gql_query';
import * as i18n from '../translations';
const AuthenticationTableManage = manageQuery(AuthenticationTable);
const AuthenticationsOverTimeManage = manageQuery(AuthenticationsOverTimeHistogram);
const ID = 'authenticationsOverTimeQuery';
const authStackByOptions: MatrixHistogramOption[] = [
{
text: i18n.NAVIGATION_AUTHENTICATIONS_STACK_BY_EVENT_TYPE,
value: 'event.type',
},
];
enum AuthMatrixDataGroup {
authSuccess = 'authentication_success',
authFailure = 'authentication_failure',
}
export const authMatrixDataMappingFields: MatrixHistogramMappingTypes = {
[AuthMatrixDataGroup.authSuccess]: {
key: AuthMatrixDataGroup.authSuccess,
value: null,
color: KpiHostsChartColors.authSuccess,
},
[AuthMatrixDataGroup.authFailure]: {
key: AuthMatrixDataGroup.authFailure,
value: null,
color: KpiHostsChartColors.authFailure,
},
};
export const AuthenticationsQueryTabBody = ({
deleteQuery,
@ -27,68 +57,75 @@ export const AuthenticationsQueryTabBody = ({
startDate,
type,
updateDateRange = () => {},
}: HostsComponentsQueryProps) => (
<>
<AuthenticationsOverTimeQuery
endDate={endDate}
filterQuery={filterQuery}
sourceId="default"
startDate={startDate}
type={hostsModel.HostsType.page}
>
{({ authenticationsOverTime, loading, id, inspect, refetch, totalCount }) => (
<AuthenticationsOverTimeManage
data={authenticationsOverTime!}
endDate={endDate}
id={id}
inspect={inspect}
loading={loading}
refetch={refetch}
setQuery={setQuery}
startDate={startDate}
totalCount={totalCount}
updateDateRange={updateDateRange}
/>
)}
</AuthenticationsOverTimeQuery>
<EuiSpacer size="l" />
<AuthenticationsQuery
endDate={endDate}
filterQuery={filterQuery}
skip={skip}
sourceId="default"
startDate={startDate}
type={type}
>
{({
authentications,
totalCount,
loading,
pageInfo,
loadPage,
id,
inspect,
isInspected,
refetch,
}) => (
<AuthenticationTableManage
data={authentications}
deleteQuery={deleteQuery}
fakeTotalCount={getOr(50, 'fakeTotalCount', pageInfo)}
id={id}
inspect={inspect}
isInspect={isInspected}
loading={loading}
loadPage={loadPage}
refetch={refetch}
showMorePagesIndicator={getOr(false, 'showMorePagesIndicator', pageInfo)}
setQuery={setQuery}
totalCount={totalCount}
type={type}
/>
)}
</AuthenticationsQuery>
</>
);
}: HostsComponentsQueryProps) => {
useEffect(() => {
return () => {
if (deleteQuery) {
deleteQuery({ id: ID });
}
};
}, [deleteQuery]);
return (
<>
<MatrixHistogramContainer
isAuthenticationsHistogram={true}
dataKey="AuthenticationsHistogram"
defaultStackByOption={authStackByOptions[0]}
deleteQuery={deleteQuery}
endDate={endDate}
errorMessage={i18n.ERROR_FETCHING_AUTHENTICATIONS_DATA}
filterQuery={filterQuery}
id={ID}
mapping={authMatrixDataMappingFields}
query={MatrixHistogramGqlQuery}
setQuery={setQuery}
skip={skip}
sourceId="default"
startDate={startDate}
stackByOptions={authStackByOptions}
title={i18n.NAVIGATION_AUTHENTICATIONS_TITLE}
type={hostsModel.HostsType.page}
updateDateRange={updateDateRange}
/>
<EuiSpacer size="l" />
<AuthenticationsQuery
endDate={endDate}
filterQuery={filterQuery}
skip={skip}
sourceId="default"
startDate={startDate}
type={type}
>
{({
authentications,
totalCount,
loading,
pageInfo,
loadPage,
id,
inspect,
isInspected,
refetch,
}) => (
<AuthenticationTableManage
data={authentications}
deleteQuery={deleteQuery}
fakeTotalCount={getOr(50, 'fakeTotalCount', pageInfo)}
id={id}
inspect={inspect}
isInspect={isInspected}
loading={loading}
loadPage={loadPage}
refetch={refetch}
showMorePagesIndicator={getOr(false, 'showMorePagesIndicator', pageInfo)}
setQuery={setQuery}
totalCount={totalCount}
type={type}
/>
)}
</AuthenticationsQuery>
</>
);
};
AuthenticationsQueryTabBody.displayName = 'AuthenticationsQueryTabBody';

View file

@ -4,50 +4,64 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { useEffect } from 'react';
import { EuiSpacer } from '@elastic/eui';
import { StatefulEventsViewer } from '../../../components/events_viewer';
import { HostsComponentsQueryProps } from './types';
import { manageQuery } from '../../../components/page/manage_query';
import { EventsOverTimeHistogram } from '../../../components/page/hosts/events_over_time';
import { EventsOverTimeQuery } from '../../../containers/events/events_over_time';
import { hostsModel } from '../../../store/hosts';
import { eventsDefaultModel } from '../../../components/events_viewer/default_model';
import { MatrixHistogramOption } from '../../../components/matrix_histogram/types';
import { MatrixHistogramContainer } from '../../../containers/matrix_histogram';
import { MatrixHistogramGqlQuery } from '../../../containers/matrix_histogram/index.gql_query';
import * as i18n from '../translations';
const HOSTS_PAGE_TIMELINE_ID = 'hosts-page';
const EventsOverTimeManage = manageQuery(EventsOverTimeHistogram);
const EVENTS_HISTOGRAM_ID = 'eventsOverTimeQuery';
const eventsStackByOptions: MatrixHistogramOption[] = [
{
text: i18n.NAVIGATION_EVENTS_STACK_BY_EVENT_ACTION,
value: 'event.action',
},
];
export const EventsQueryTabBody = ({
deleteQuery,
endDate,
filterQuery,
setQuery,
skip,
startDate,
updateDateRange = () => {},
}: HostsComponentsQueryProps) => {
useEffect(() => {
return () => {
if (deleteQuery) {
deleteQuery({ id: EVENTS_HISTOGRAM_ID });
}
};
}, [deleteQuery]);
return (
<>
<EventsOverTimeQuery
<MatrixHistogramContainer
dataKey="EventsHistogram"
defaultStackByOption={eventsStackByOptions[0]}
deleteQuery={deleteQuery}
endDate={endDate}
isEventsType={true}
errorMessage={i18n.ERROR_FETCHING_EVENTS_DATA}
filterQuery={filterQuery}
query={MatrixHistogramGqlQuery}
setQuery={setQuery}
skip={skip}
sourceId="default"
stackByOptions={eventsStackByOptions}
startDate={startDate}
type={hostsModel.HostsType.page}
>
{({ eventsOverTime, loading, id, inspect, refetch, totalCount }) => (
<EventsOverTimeManage
data={eventsOverTime!}
endDate={endDate}
id={id}
inspect={inspect}
loading={loading}
refetch={refetch}
setQuery={setQuery}
startDate={startDate}
totalCount={totalCount}
updateDateRange={updateDateRange}
/>
)}
</EventsOverTimeQuery>
title={i18n.NAVIGATION_EVENTS_TITLE}
updateDateRange={updateDateRange}
id={EVENTS_HISTOGRAM_ID}
/>
<EuiSpacer size="l" />
<StatefulEventsViewer
defaultModel={eventsDefaultModel}

View file

@ -28,6 +28,13 @@ export const NAVIGATION_AUTHENTICATIONS_TITLE = i18n.translate(
}
);
export const NAVIGATION_AUTHENTICATIONS_STACK_BY_EVENT_TYPE = i18n.translate(
'xpack.siem.hosts.navigation.authentications.stackByEventType',
{
defaultMessage: 'event type',
}
);
export const NAVIGATION_UNCOMMON_PROCESSES_TITLE = i18n.translate(
'xpack.siem.hosts.navigation.uncommonProcessesTitle',
{
@ -46,6 +53,13 @@ export const NAVIGATION_EVENTS_TITLE = i18n.translate('xpack.siem.hosts.navigati
defaultMessage: 'Events',
});
export const NAVIGATION_EVENTS_STACK_BY_EVENT_ACTION = i18n.translate(
'xpack.siem.hosts.navigation.eventsStackByEventAction',
{
defaultMessage: 'action',
}
);
export const NAVIGATION_ALERTS_TITLE = i18n.translate('xpack.siem.hosts.navigation.alertsTitle', {
defaultMessage: 'Alerts',
});
@ -62,3 +76,17 @@ export const EMPTY_ACTION_PRIMARY = i18n.translate('xpack.siem.hosts.emptyAction
export const EMPTY_ACTION_SECONDARY = i18n.translate('xpack.siem.hosts.emptyActionSecondary', {
defaultMessage: 'Go to documentation',
});
export const ERROR_FETCHING_AUTHENTICATIONS_DATA = i18n.translate(
'xpack.siem.hosts.navigaton.matrixHistogram.errorFetchingAuthenticationsData',
{
defaultMessage: 'Failed to query authentications data',
}
);
export const ERROR_FETCHING_EVENTS_DATA = i18n.translate(
'xpack.siem.hosts.navigaton.matrixHistogram.errorFetchingEventsData',
{
defaultMessage: 'Failed to query events data',
}
);

View file

@ -4,21 +4,37 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { useEffect } from 'react';
import { getOr } from 'lodash/fp';
import { EuiSpacer } from '@elastic/eui';
import { ScaleType } from '@elastic/charts';
import { NetworkDnsTable } from '../../../components/page/network/network_dns_table';
import { NetworkDnsQuery, NetworkDnsHistogramQuery } from '../../../containers/network_dns';
import {
NetworkDnsQuery,
NetworkDnsHistogramQuery,
HISTOGRAM_ID,
} from '../../../containers/network_dns';
import { manageQuery } from '../../../components/page/manage_query';
import { NetworkComponentQueryProps } from './types';
import { NetworkDnsHistogram } from '../../../components/page/network/dns_histogram';
import { networkModel } from '../../../store';
import { MatrixHistogramOption } from '../../../components/matrix_histogram/types';
import { networkDnsQuery } from '../../../containers/network_dns/index.gql_query';
import * as i18n from '../translations';
import { useFormatBytes } from '../../../components/formatted_bytes';
const NetworkDnsTableManage = manageQuery(NetworkDnsTable);
const NetworkDnsHistogramManage = manageQuery(NetworkDnsHistogram);
const dnsStackByOptions: MatrixHistogramOption[] = [
{
text: i18n.NAVIGATION_DNS_STACK_BY_DOMAIN,
value: 'dns.question.registered_domain',
},
];
export const DnsQueryTabBody = ({
deleteQuery,
endDate,
filterQuery,
skip,
@ -26,32 +42,17 @@ export const DnsQueryTabBody = ({
setQuery,
type,
updateDateRange = () => {},
}: NetworkComponentQueryProps) => (
<>
<NetworkDnsHistogramQuery
endDate={endDate}
filterQuery={filterQuery}
skip={skip}
sourceId="default"
startDate={startDate}
type={type}
>
{({ totalCount, loading, id, inspect, refetch, histogram }) => (
<NetworkDnsHistogramManage
id={id}
loading={loading}
data={histogram}
endDate={endDate}
startDate={startDate}
inspect={inspect}
refetch={refetch}
setQuery={setQuery}
totalCount={totalCount}
updateDateRange={updateDateRange}
/>
)}
</NetworkDnsHistogramQuery>
<EuiSpacer />
}: NetworkComponentQueryProps) => {
useEffect(() => {
return () => {
if (deleteQuery) {
deleteQuery({ id: HISTOGRAM_ID });
}
};
}, [deleteQuery]);
const formatBytes = useFormatBytes();
return (
<NetworkDnsQuery
endDate={endDate}
filterQuery={filterQuery}
@ -70,25 +71,47 @@ export const DnsQueryTabBody = ({
inspect,
isInspected,
refetch,
histogram,
}) => (
<NetworkDnsTableManage
data={networkDns}
fakeTotalCount={getOr(50, 'fakeTotalCount', pageInfo)}
id={id}
inspect={inspect}
isInspect={isInspected}
loading={loading}
loadPage={loadPage}
refetch={refetch}
setQuery={setQuery}
showMorePagesIndicator={getOr(false, 'showMorePagesIndicator', pageInfo)}
totalCount={totalCount}
type={type}
/>
<>
<NetworkDnsHistogramQuery
dataKey={['NetworkDns', 'histogram']}
defaultStackByOption={dnsStackByOptions[0]}
endDate={endDate}
errorMessage={i18n.ERROR_FETCHING_DNS_DATA}
filterQuery={filterQuery}
isDNSHistogram={true}
limit={totalCount}
query={networkDnsQuery}
scaleType={ScaleType.Ordinal}
setQuery={setQuery}
sourceId="default"
startDate={startDate}
stackByOptions={dnsStackByOptions}
title={i18n.NAVIGATION_DNS_TITLE}
type={networkModel.NetworkType.page}
updateDateRange={updateDateRange}
yTickFormatter={formatBytes}
showLegend={false}
/>
<EuiSpacer />
<NetworkDnsTableManage
data={networkDns}
fakeTotalCount={getOr(50, 'fakeTotalCount', pageInfo)}
id={id}
inspect={inspect}
isInspect={isInspected}
loading={loading}
loadPage={loadPage}
refetch={refetch}
setQuery={setQuery}
showMorePagesIndicator={getOr(false, 'showMorePagesIndicator', pageInfo)}
totalCount={totalCount}
type={type}
/>
</>
)}
</NetworkDnsQuery>
</>
);
);
};
DnsQueryTabBody.displayName = 'DNSQueryTabBody';

View file

@ -133,14 +133,14 @@ const NetworkComponent = React.memo<NetworkComponentProps>(
<EuiSpacer />
<NetworkRoutes
to={to}
filterQuery={filterQuery}
isInitializing={isInitializing}
from={from}
type={networkModel.NetworkType.page}
isInitializing={isInitializing}
indexPattern={indexPattern}
setQuery={setQuery}
setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker}
type={networkModel.NetworkType.page}
to={to}
networkPagePath={networkPagePath}
/>
</>

View file

@ -35,6 +35,20 @@ export const NAVIGATION_DNS_TITLE = i18n.translate('xpack.siem.network.navigatio
defaultMessage: 'DNS',
});
export const NAVIGATION_DNS_STACK_BY_DOMAIN = i18n.translate(
'xpack.siem.hosts.navigation.dns.stackByDomain',
{
defaultMessage: 'domain',
}
);
export const ERROR_FETCHING_DNS_DATA = i18n.translate(
'xpack.siem.hosts.navigation.dns.histogram.errorFetchingDnsData',
{
defaultMessage: 'Failed to query DNS data',
}
);
export const NAVIGATION_TLS_TITLE = i18n.translate('xpack.siem.network.navigation.tlsTitle', {
defaultMessage: 'TLS',
});

View file

@ -31,6 +31,7 @@ export const createAlertsResolvers = (
const options = {
...createOptions(source, args, info),
defaultIndex: args.defaultIndex,
stackByField: args.stackByField,
};
return libs.alerts.getAlertsHistogramData(req, options);
},

View file

@ -9,7 +9,7 @@ import gql from 'graphql-tag';
export const alertsSchema = gql`
type AlertsOverTimeData {
inspect: Inspect
alertsOverTimeByModule: [MatrixOverTimeHistogramData!]!
matrixHistogramData: [MatrixOverTimeHistogramData!]!
totalCount: Float!
}
@ -18,6 +18,7 @@ export const alertsSchema = gql`
filterQuery: String
defaultIndex: [String!]!
timerange: TimerangeInput!
stackByField: String
): AlertsOverTimeData!
}
`;

View file

@ -15,7 +15,7 @@ export interface AnomaliesResolversDeps {
}
type QueryAnomaliesOverTimeResolver = ChildResolverOf<
AppResolverOf<SourceResolvers.AnomaliesOverTimeResolver>,
AppResolverOf<SourceResolvers.AnomaliesHistogramResolver>,
QuerySourceResolver
>;
@ -23,14 +23,15 @@ export const createAnomaliesResolvers = (
libs: AnomaliesResolversDeps
): {
Source: {
AnomaliesOverTime: QueryAnomaliesOverTimeResolver;
AnomaliesHistogram: QueryAnomaliesOverTimeResolver;
};
} => ({
Source: {
async AnomaliesOverTime(source, args, { req }, info) {
async AnomaliesHistogram(source, args, { req }, info) {
const options = {
...createOptions(source, args, info),
defaultIndex: args.defaultIndex,
stackByField: args.stackByField,
};
return libs.anomalies.getAnomaliesOverTime(req, options);
},

View file

@ -9,15 +9,16 @@ import gql from 'graphql-tag';
export const anomaliesSchema = gql`
type AnomaliesOverTimeData {
inspect: Inspect
anomaliesOverTime: [MatrixOverTimeHistogramData!]!
matrixHistogramData: [MatrixOverTimeHistogramData!]!
totalCount: Float!
}
extend type Source {
AnomaliesOverTime(
AnomaliesHistogram(
timerange: TimerangeInput!
filterQuery: String
defaultIndex: [String!]!
stackByField: String
): AnomaliesOverTimeData!
}
`;

View file

@ -16,7 +16,7 @@ type QueryAuthenticationsResolver = ChildResolverOf<
>;
type QueryAuthenticationsOverTimeResolver = ChildResolverOf<
AppResolverOf<SourceResolvers.AuthenticationsOverTimeResolver>,
AppResolverOf<SourceResolvers.AuthenticationsHistogramResolver>,
QuerySourceResolver
>;
@ -29,7 +29,7 @@ export const createAuthenticationsResolvers = (
): {
Source: {
Authentications: QueryAuthenticationsResolver;
AuthenticationsOverTime: QueryAuthenticationsOverTimeResolver;
AuthenticationsHistogram: QueryAuthenticationsOverTimeResolver;
};
} => ({
Source: {
@ -37,10 +37,11 @@ export const createAuthenticationsResolvers = (
const options = createOptionsPaginated(source, args, info);
return libs.authentications.getAuthentications(req, options);
},
async AuthenticationsOverTime(source, args, { req }, info) {
async AuthenticationsHistogram(source, args, { req }, info) {
const options = {
...createOptions(source, args, info),
defaultIndex: args.defaultIndex,
stackByField: args.stackByField,
};
return libs.authentications.getAuthenticationsOverTime(req, options);
},

View file

@ -36,7 +36,7 @@ export const authenticationsSchema = gql`
type AuthenticationsOverTimeData {
inspect: Inspect
authenticationsOverTime: [MatrixOverTimeHistogramData!]!
matrixHistogramData: [MatrixOverTimeHistogramData!]!
totalCount: Float!
}
@ -48,10 +48,11 @@ export const authenticationsSchema = gql`
filterQuery: String
defaultIndex: [String!]!
): AuthenticationsData!
AuthenticationsOverTime(
AuthenticationsHistogram(
timerange: TimerangeInput!
filterQuery: String
defaultIndex: [String!]!
stackByField: String
): AuthenticationsOverTimeData!
}
`;

View file

@ -33,7 +33,7 @@ export interface EventsResolversDeps {
}
type QueryEventsOverTimeResolver = ChildResolverOf<
AppResolverOf<SourceResolvers.EventsOverTimeResolver>,
AppResolverOf<SourceResolvers.EventsHistogramResolver>,
QuerySourceResolver
>;
@ -44,7 +44,7 @@ export const createEventsResolvers = (
Timeline: QueryTimelineResolver;
TimelineDetails: QueryTimelineDetailsResolver;
LastEventTime: QueryLastEventTimeResolver;
EventsOverTime: QueryEventsOverTimeResolver;
EventsHistogram: QueryEventsOverTimeResolver;
};
} => ({
Source: {
@ -71,10 +71,11 @@ export const createEventsResolvers = (
};
return libs.events.getLastEventTimeData(req, options);
},
async EventsOverTime(source, args, { req }, info) {
async EventsHistogram(source, args, { req }, info) {
const options = {
...createOptions(source, args, info),
defaultIndex: args.defaultIndex,
stackByField: args.stackByField,
};
return libs.events.getEventsOverTime(req, options);
},

View file

@ -76,7 +76,7 @@ export const eventsSchema = gql`
type EventsOverTimeData {
inspect: Inspect
eventsOverTime: [MatrixOverTimeHistogramData!]!
matrixHistogramData: [MatrixOverTimeHistogramData!]!
totalCount: Float!
}
@ -100,10 +100,11 @@ export const eventsSchema = gql`
details: LastTimeDetails!
defaultIndex: [String!]!
): LastEventTimeData!
EventsOverTime(
EventsHistogram(
timerange: TimerangeInput!
filterQuery: String
defaultIndex: [String!]!
stackByField: String
): EventsOverTimeData!
}
`;

View file

@ -76,6 +76,7 @@ export const createNetworkResolvers = (
...createOptionsPaginated(source, args, info),
networkDnsSortField: args.sort,
isPtrIncluded: args.isPtrIncluded,
stackByField: args.stackByField,
};
return libs.network.getNetworkDns(req, options);
},

View file

@ -223,6 +223,7 @@ export const networkSchema = gql`
isPtrIncluded: Boolean!
pagination: PaginationInputPaginated!
sort: NetworkDnsSortField!
stackByField: String
timerange: TimerangeInput!
defaultIndex: [String!]!
): NetworkDnsData!

View file

@ -461,11 +461,11 @@ export interface Source {
AlertsHistogram: AlertsOverTimeData;
AnomaliesOverTime: AnomaliesOverTimeData;
AnomaliesHistogram: AnomaliesOverTimeData;
/** Gets Authentication success and failures based on a timerange */
Authentications: AuthenticationsData;
AuthenticationsOverTime: AuthenticationsOverTimeData;
AuthenticationsHistogram: AuthenticationsOverTimeData;
Timeline: TimelineData;
@ -473,7 +473,7 @@ export interface Source {
LastEventTime: LastEventTimeData;
EventsOverTime: EventsOverTimeData;
EventsHistogram: EventsOverTimeData;
/** Gets Hosts based on timerange and specified criteria, or all events in the timerange if no criteria is specified */
Hosts: HostsData;
@ -565,7 +565,7 @@ export interface IndexField {
export interface AlertsOverTimeData {
inspect?: Maybe<Inspect>;
alertsOverTimeByModule: MatrixOverTimeHistogramData[];
matrixHistogramData: MatrixOverTimeHistogramData[];
totalCount: number;
}
@ -587,7 +587,7 @@ export interface MatrixOverTimeHistogramData {
export interface AnomaliesOverTimeData {
inspect?: Maybe<Inspect>;
anomaliesOverTime: MatrixOverTimeHistogramData[];
matrixHistogramData: MatrixOverTimeHistogramData[];
totalCount: number;
}
@ -729,7 +729,7 @@ export interface PageInfoPaginated {
export interface AuthenticationsOverTimeData {
inspect?: Maybe<Inspect>;
authenticationsOverTime: MatrixOverTimeHistogramData[];
matrixHistogramData: MatrixOverTimeHistogramData[];
totalCount: number;
}
@ -1313,7 +1313,7 @@ export interface LastEventTimeData {
export interface EventsOverTimeData {
inspect?: Maybe<Inspect>;
eventsOverTime: MatrixOverTimeHistogramData[];
matrixHistogramData: MatrixOverTimeHistogramData[];
totalCount: number;
}
@ -2155,13 +2155,17 @@ export interface AlertsHistogramSourceArgs {
defaultIndex: string[];
timerange: TimerangeInput;
stackByField?: Maybe<string>;
}
export interface AnomaliesOverTimeSourceArgs {
export interface AnomaliesHistogramSourceArgs {
timerange: TimerangeInput;
filterQuery?: Maybe<string>;
defaultIndex: string[];
stackByField?: Maybe<string>;
}
export interface AuthenticationsSourceArgs {
timerange: TimerangeInput;
@ -2172,12 +2176,14 @@ export interface AuthenticationsSourceArgs {
defaultIndex: string[];
}
export interface AuthenticationsOverTimeSourceArgs {
export interface AuthenticationsHistogramSourceArgs {
timerange: TimerangeInput;
filterQuery?: Maybe<string>;
defaultIndex: string[];
stackByField?: Maybe<string>;
}
export interface TimelineSourceArgs {
pagination: PaginationInput;
@ -2208,12 +2214,14 @@ export interface LastEventTimeSourceArgs {
defaultIndex: string[];
}
export interface EventsOverTimeSourceArgs {
export interface EventsHistogramSourceArgs {
timerange: TimerangeInput;
filterQuery?: Maybe<string>;
defaultIndex: string[];
stackByField?: Maybe<string>;
}
export interface HostsSourceArgs {
id?: Maybe<string>;
@ -2342,6 +2350,8 @@ export interface NetworkDnsSourceArgs {
sort: NetworkDnsSortField;
stackByField?: Maybe<string>;
timerange: TimerangeInput;
defaultIndex: string[];
@ -2800,11 +2810,11 @@ export namespace SourceResolvers {
AlertsHistogram?: AlertsHistogramResolver<AlertsOverTimeData, TypeParent, TContext>;
AnomaliesOverTime?: AnomaliesOverTimeResolver<AnomaliesOverTimeData, TypeParent, TContext>;
AnomaliesHistogram?: AnomaliesHistogramResolver<AnomaliesOverTimeData, TypeParent, TContext>;
/** Gets Authentication success and failures based on a timerange */
Authentications?: AuthenticationsResolver<AuthenticationsData, TypeParent, TContext>;
AuthenticationsOverTime?: AuthenticationsOverTimeResolver<
AuthenticationsHistogram?: AuthenticationsHistogramResolver<
AuthenticationsOverTimeData,
TypeParent,
TContext
@ -2816,7 +2826,7 @@ export namespace SourceResolvers {
LastEventTime?: LastEventTimeResolver<LastEventTimeData, TypeParent, TContext>;
EventsOverTime?: EventsOverTimeResolver<EventsOverTimeData, TypeParent, TContext>;
EventsHistogram?: EventsHistogramResolver<EventsOverTimeData, TypeParent, TContext>;
/** Gets Hosts based on timerange and specified criteria, or all events in the timerange if no criteria is specified */
Hosts?: HostsResolver<HostsData, TypeParent, TContext>;
@ -2883,19 +2893,23 @@ export namespace SourceResolvers {
defaultIndex: string[];
timerange: TimerangeInput;
stackByField?: Maybe<string>;
}
export type AnomaliesOverTimeResolver<
export type AnomaliesHistogramResolver<
R = AnomaliesOverTimeData,
Parent = Source,
TContext = SiemContext
> = Resolver<R, Parent, TContext, AnomaliesOverTimeArgs>;
export interface AnomaliesOverTimeArgs {
> = Resolver<R, Parent, TContext, AnomaliesHistogramArgs>;
export interface AnomaliesHistogramArgs {
timerange: TimerangeInput;
filterQuery?: Maybe<string>;
defaultIndex: string[];
stackByField?: Maybe<string>;
}
export type AuthenticationsResolver<
@ -2913,17 +2927,19 @@ export namespace SourceResolvers {
defaultIndex: string[];
}
export type AuthenticationsOverTimeResolver<
export type AuthenticationsHistogramResolver<
R = AuthenticationsOverTimeData,
Parent = Source,
TContext = SiemContext
> = Resolver<R, Parent, TContext, AuthenticationsOverTimeArgs>;
export interface AuthenticationsOverTimeArgs {
> = Resolver<R, Parent, TContext, AuthenticationsHistogramArgs>;
export interface AuthenticationsHistogramArgs {
timerange: TimerangeInput;
filterQuery?: Maybe<string>;
defaultIndex: string[];
stackByField?: Maybe<string>;
}
export type TimelineResolver<
@ -2973,17 +2989,19 @@ export namespace SourceResolvers {
defaultIndex: string[];
}
export type EventsOverTimeResolver<
export type EventsHistogramResolver<
R = EventsOverTimeData,
Parent = Source,
TContext = SiemContext
> = Resolver<R, Parent, TContext, EventsOverTimeArgs>;
export interface EventsOverTimeArgs {
> = Resolver<R, Parent, TContext, EventsHistogramArgs>;
export interface EventsHistogramArgs {
timerange: TimerangeInput;
filterQuery?: Maybe<string>;
defaultIndex: string[];
stackByField?: Maybe<string>;
}
export type HostsResolver<R = HostsData, Parent = Source, TContext = SiemContext> = Resolver<
@ -3180,6 +3198,8 @@ export namespace SourceResolvers {
sort: NetworkDnsSortField;
stackByField?: Maybe<string>;
timerange: TimerangeInput;
defaultIndex: string[];
@ -3443,7 +3463,7 @@ export namespace AlertsOverTimeDataResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = AlertsOverTimeData> {
inspect?: InspectResolver<Maybe<Inspect>, TypeParent, TContext>;
alertsOverTimeByModule?: AlertsOverTimeByModuleResolver<
matrixHistogramData?: MatrixHistogramDataResolver<
MatrixOverTimeHistogramData[],
TypeParent,
TContext
@ -3457,7 +3477,7 @@ export namespace AlertsOverTimeDataResolvers {
Parent = AlertsOverTimeData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type AlertsOverTimeByModuleResolver<
export type MatrixHistogramDataResolver<
R = MatrixOverTimeHistogramData[],
Parent = AlertsOverTimeData,
TContext = SiemContext
@ -3518,7 +3538,7 @@ export namespace AnomaliesOverTimeDataResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = AnomaliesOverTimeData> {
inspect?: InspectResolver<Maybe<Inspect>, TypeParent, TContext>;
anomaliesOverTime?: AnomaliesOverTimeResolver<
matrixHistogramData?: MatrixHistogramDataResolver<
MatrixOverTimeHistogramData[],
TypeParent,
TContext
@ -3532,7 +3552,7 @@ export namespace AnomaliesOverTimeDataResolvers {
Parent = AnomaliesOverTimeData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type AnomaliesOverTimeResolver<
export type MatrixHistogramDataResolver<
R = MatrixOverTimeHistogramData[],
Parent = AnomaliesOverTimeData,
TContext = SiemContext
@ -3993,7 +4013,7 @@ export namespace AuthenticationsOverTimeDataResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = AuthenticationsOverTimeData> {
inspect?: InspectResolver<Maybe<Inspect>, TypeParent, TContext>;
authenticationsOverTime?: AuthenticationsOverTimeResolver<
matrixHistogramData?: MatrixHistogramDataResolver<
MatrixOverTimeHistogramData[],
TypeParent,
TContext
@ -4007,7 +4027,7 @@ export namespace AuthenticationsOverTimeDataResolvers {
Parent = AuthenticationsOverTimeData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type AuthenticationsOverTimeResolver<
export type MatrixHistogramDataResolver<
R = MatrixOverTimeHistogramData[],
Parent = AuthenticationsOverTimeData,
TContext = SiemContext
@ -5947,7 +5967,11 @@ export namespace EventsOverTimeDataResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = EventsOverTimeData> {
inspect?: InspectResolver<Maybe<Inspect>, TypeParent, TContext>;
eventsOverTime?: EventsOverTimeResolver<MatrixOverTimeHistogramData[], TypeParent, TContext>;
matrixHistogramData?: MatrixHistogramDataResolver<
MatrixOverTimeHistogramData[],
TypeParent,
TContext
>;
totalCount?: TotalCountResolver<number, TypeParent, TContext>;
}
@ -5957,7 +5981,7 @@ export namespace EventsOverTimeDataResolvers {
Parent = EventsOverTimeData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type EventsOverTimeResolver<
export type MatrixHistogramDataResolver<
R = MatrixOverTimeHistogramData[],
Parent = EventsOverTimeData,
TContext = SiemContext

View file

@ -10,7 +10,7 @@ import { AlertsOverTimeData, MatrixOverTimeHistogramData } from '../../graphql/t
import { inspectStringifyObject } from '../../utils/build_query';
import { FrameworkAdapter, FrameworkRequest, RequestBasicOptions } from '../framework';
import { FrameworkAdapter, FrameworkRequest, MatrixHistogramRequestOptions } from '../framework';
import { buildAlertsHistogramQuery } from './query.dsl';
import { AlertsAdapter, AlertsGroupData, AlertsBucket } from './types';
@ -22,7 +22,7 @@ export class ElasticsearchAlertsAdapter implements AlertsAdapter {
public async getAlertsHistogramData(
request: FrameworkRequest,
options: RequestBasicOptions
options: MatrixHistogramRequestOptions
): Promise<AlertsOverTimeData> {
const dsl = buildAlertsHistogramQuery(options);
const response = await this.framework.callWithRequest<EventHit, TermAggregation>(
@ -31,14 +31,14 @@ export class ElasticsearchAlertsAdapter implements AlertsAdapter {
dsl
);
const totalCount = getOr(0, 'hits.total.value', response);
const alertsOverTimeByModule = getOr([], 'aggregations.alertsByModuleGroup.buckets', response);
const matrixHistogramData = getOr([], 'aggregations.alertsByModuleGroup.buckets', response);
const inspect = {
dsl: [inspectStringifyObject(dsl)],
response: [inspectStringifyObject(response)],
};
return {
inspect,
alertsOverTimeByModule: getAlertsOverTimeByModule(alertsOverTimeByModule),
matrixHistogramData: getAlertsOverTimeByModule(matrixHistogramData),
totalCount,
};
}

View file

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { FrameworkAdapter, FrameworkRequest, RequestBasicOptions } from '../framework';
import { FrameworkAdapter, FrameworkRequest, MatrixHistogramRequestOptions } from '../framework';
import expect from '@kbn/expect';
import { ElasticsearchAlertsAdapter } from './elasticsearch_adapter';
@ -41,11 +41,11 @@ describe('alerts elasticsearch_adapter', () => {
const EsNetworkTimelineAlerts = new ElasticsearchAlertsAdapter(mockFramework);
const data = await EsNetworkTimelineAlerts.getAlertsHistogramData(
(mockRequest as unknown) as FrameworkRequest,
(mockOptions as unknown) as RequestBasicOptions
(mockOptions as unknown) as MatrixHistogramRequestOptions
);
expect(data).to.eql({
alertsOverTimeByModule: mockAlertsHistogramDataFormattedResponse,
matrixHistogramData: mockAlertsHistogramDataFormattedResponse,
inspect: {
dsl: ['"mockAlertsHistogramQueryDsl"'],
response: [JSON.stringify(mockAlertsHistogramDataResponse, null, 2)],

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { FrameworkRequest, RequestBasicOptions } from '../framework';
import { FrameworkRequest, MatrixHistogramRequestOptions } from '../framework';
export * from './elasticsearch_adapter';
import { AlertsAdapter } from './types';
import { AlertsOverTimeData } from '../../graphql/types';
@ -14,7 +14,7 @@ export class Alerts {
public async getAlertsHistogramData(
req: FrameworkRequest,
options: RequestBasicOptions
options: MatrixHistogramRequestOptions
): Promise<AlertsOverTimeData> {
return this.adapter.getAlertsHistogramData(req, options);
}

View file

@ -6,7 +6,7 @@
import { createQueryFilterClauses, calculateTimeseriesInterval } from '../../utils/build_query';
import { buildTimelineQuery } from '../events/query.dsl';
import { RequestOptions, RequestBasicOptions } from '../framework';
import { RequestOptions, MatrixHistogramRequestOptions } from '../framework';
export const buildAlertsQuery = (options: RequestOptions) => {
const eventsQuery = buildTimelineQuery(options);
@ -35,7 +35,8 @@ export const buildAlertsHistogramQuery = ({
sourceConfiguration: {
fields: { timestamp },
},
}: RequestBasicOptions) => {
stackByField,
}: MatrixHistogramRequestOptions) => {
const filter = [
...createQueryFilterClauses(filterQuery),
{
@ -84,7 +85,7 @@ export const buildAlertsHistogramQuery = ({
return {
alertsByModuleGroup: {
terms: {
field: 'event.module',
field: stackByField,
missing: 'All others',
order: {
_count: 'desc',

View file

@ -5,7 +5,7 @@
*/
import { AlertsOverTimeData } from '../../graphql/types';
import { FrameworkRequest, RequestBasicOptions } from '../framework';
import { FrameworkRequest, MatrixHistogramRequestOptions } from '../framework';
export interface AlertsBucket {
key: number;
@ -22,6 +22,6 @@ export interface AlertsGroupData {
export interface AlertsAdapter {
getAlertsHistogramData(
request: FrameworkRequest,
options: RequestBasicOptions
options: MatrixHistogramRequestOptions
): Promise<AlertsOverTimeData>;
}

View file

@ -8,7 +8,7 @@ import { getOr } from 'lodash/fp';
import { AnomaliesOverTimeData } from '../../graphql/types';
import { inspectStringifyObject } from '../../utils/build_query';
import { FrameworkAdapter, FrameworkRequest, RequestBasicOptions } from '../framework';
import { FrameworkAdapter, FrameworkRequest, MatrixHistogramRequestOptions } from '../framework';
import { TermAggregation } from '../types';
import { AnomalyHit, AnomaliesAdapter, AnomaliesActionGroupData } from './types';
@ -20,7 +20,7 @@ export class ElasticsearchAnomaliesAdapter implements AnomaliesAdapter {
public async getAnomaliesOverTime(
request: FrameworkRequest,
options: RequestBasicOptions
options: MatrixHistogramRequestOptions
): Promise<AnomaliesOverTimeData> {
const dsl = buildAnomaliesOverTimeQuery(options);
@ -39,7 +39,7 @@ export class ElasticsearchAnomaliesAdapter implements AnomaliesAdapter {
};
return {
inspect,
anomaliesOverTime: getAnomaliesOverTimeByJobId(anomaliesOverTimeBucket),
matrixHistogramData: getAnomaliesOverTimeByJobId(anomaliesOverTimeBucket),
totalCount,
};
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { FrameworkRequest, RequestBasicOptions } from '../framework';
import { FrameworkRequest, MatrixHistogramRequestOptions } from '../framework';
export * from './elasticsearch_adapter';
import { AnomaliesAdapter } from './types';
import { AnomaliesOverTimeData } from '../../../public/graphql/types';
@ -14,7 +14,7 @@ export class Anomalies {
public async getAnomaliesOverTime(
req: FrameworkRequest,
options: RequestBasicOptions
options: MatrixHistogramRequestOptions
): Promise<AnomaliesOverTimeData> {
return this.adapter.getAnomaliesOverTime(req, options);
}

View file

@ -5,13 +5,14 @@
*/
import { createQueryFilterClauses, calculateTimeseriesInterval } from '../../utils/build_query';
import { RequestBasicOptions } from '../framework';
import { MatrixHistogramRequestOptions } from '../framework';
export const buildAnomaliesOverTimeQuery = ({
filterQuery,
timerange: { from, to },
defaultIndex,
}: RequestBasicOptions) => {
stackByField = 'job_id',
}: MatrixHistogramRequestOptions) => {
const filter = [
...createQueryFilterClauses(filterQuery),
{
@ -42,7 +43,7 @@ export const buildAnomaliesOverTimeQuery = ({
return {
anomalyActionGroup: {
terms: {
field: 'job_id',
field: stackByField,
order: {
_count: 'desc',
},

View file

@ -5,13 +5,13 @@
*/
import { AnomaliesOverTimeData } from '../../graphql/types';
import { FrameworkRequest, RequestBasicOptions } from '../framework';
import { FrameworkRequest, MatrixHistogramRequestOptions } from '../framework';
import { SearchHit } from '../types';
export interface AnomaliesAdapter {
getAnomaliesOverTime(
req: FrameworkRequest,
options: RequestBasicOptions
options: MatrixHistogramRequestOptions
): Promise<AnomaliesOverTimeData>;
}

View file

@ -17,7 +17,7 @@ import {
FrameworkAdapter,
FrameworkRequest,
RequestOptionsPaginated,
RequestBasicOptions,
MatrixHistogramRequestOptions,
} from '../framework';
import { TermAggregation } from '../types';
import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../common/constants';
@ -112,7 +112,7 @@ export class ElasticsearchAuthenticationAdapter implements AuthenticationsAdapte
public async getAuthenticationsOverTime(
request: FrameworkRequest,
options: RequestBasicOptions
options: MatrixHistogramRequestOptions
): Promise<AuthenticationsOverTimeData> {
const dsl = buildAuthenticationsOverTimeQuery(options);
const response = await this.framework.callWithRequest<AuthenticationHit, TermAggregation>(
@ -132,7 +132,7 @@ export class ElasticsearchAuthenticationAdapter implements AuthenticationsAdapte
};
return {
inspect,
authenticationsOverTime: getAuthenticationsOverTimeByAuthenticationResult(
matrixHistogramData: getAuthenticationsOverTimeByAuthenticationResult(
authenticationsOverTimeBucket
),
totalCount,

View file

@ -5,7 +5,11 @@
*/
import { AuthenticationsData } from '../../graphql/types';
import { FrameworkRequest, RequestOptionsPaginated, RequestBasicOptions } from '../framework';
import {
FrameworkRequest,
RequestOptionsPaginated,
MatrixHistogramRequestOptions,
} from '../framework';
import { AuthenticationsAdapter } from './types';
import { AuthenticationsOverTimeData } from '../../../public/graphql/types';
@ -22,7 +26,7 @@ export class Authentications {
public async getAuthenticationsOverTime(
req: FrameworkRequest,
options: RequestBasicOptions
options: MatrixHistogramRequestOptions
): Promise<AuthenticationsOverTimeData> {
return this.adapter.getAuthenticationsOverTime(req, options);
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { createQueryFilterClauses, calculateTimeseriesInterval } from '../../utils/build_query';
import { RequestBasicOptions } from '../framework';
import { MatrixHistogramRequestOptions } from '../framework';
export const buildAuthenticationsOverTimeQuery = ({
filterQuery,
@ -13,7 +13,8 @@ export const buildAuthenticationsOverTimeQuery = ({
sourceConfiguration: {
fields: { timestamp },
},
}: RequestBasicOptions) => {
stackByField = 'event.type',
}: MatrixHistogramRequestOptions) => {
const filter = [
...createQueryFilterClauses(filterQuery),
{
@ -44,7 +45,7 @@ export const buildAuthenticationsOverTimeQuery = ({
return {
eventActionGroup: {
terms: {
field: 'event.type',
field: stackByField,
include: ['authentication_success', 'authentication_failure'],
order: {
_count: 'desc',

View file

@ -9,7 +9,11 @@ import {
AuthenticationsOverTimeData,
LastSourceHost,
} from '../../graphql/types';
import { FrameworkRequest, RequestOptionsPaginated, RequestBasicOptions } from '../framework';
import {
FrameworkRequest,
RequestOptionsPaginated,
MatrixHistogramRequestOptions,
} from '../framework';
import { Hit, SearchHit, TotalHit } from '../types';
export interface AuthenticationsAdapter {
@ -19,7 +23,7 @@ export interface AuthenticationsAdapter {
): Promise<AuthenticationsData>;
getAuthenticationsOverTime(
req: FrameworkRequest,
options: RequestBasicOptions
options: MatrixHistogramRequestOptions
): Promise<AuthenticationsOverTimeData>;
}

View file

@ -31,7 +31,7 @@ import { baseCategoryFields } from '../../utils/beat_schema/8.0.0';
import { reduceFields } from '../../utils/build_query/reduce_fields';
import { mergeFieldsWithHit, inspectStringifyObject } from '../../utils/build_query';
import { eventFieldsMap } from '../ecs_fields';
import { FrameworkAdapter, FrameworkRequest, RequestBasicOptions } from '../framework';
import { FrameworkAdapter, FrameworkRequest, MatrixHistogramRequestOptions } from '../framework';
import { TermAggregation } from '../types';
import { buildDetailsQuery, buildTimelineQuery } from './query.dsl';
@ -131,7 +131,7 @@ export class ElasticsearchEventsAdapter implements EventsAdapter {
public async getEventsOverTime(
request: FrameworkRequest,
options: RequestBasicOptions
options: MatrixHistogramRequestOptions
): Promise<EventsOverTimeData> {
const dsl = buildEventsOverTimeQuery(options);
const response = await this.framework.callWithRequest<EventHit, TermAggregation>(
@ -147,7 +147,7 @@ export class ElasticsearchEventsAdapter implements EventsAdapter {
};
return {
inspect,
eventsOverTime: getEventsOverTimeByActionName(eventsOverTimeBucket),
matrixHistogramData: getEventsOverTimeByActionName(eventsOverTimeBucket),
totalCount,
};
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { createQueryFilterClauses, calculateTimeseriesInterval } from '../../utils/build_query';
import { RequestBasicOptions } from '../framework';
import { MatrixHistogramRequestOptions } from '../framework';
export const buildEventsOverTimeQuery = ({
filterQuery,
@ -13,7 +13,8 @@ export const buildEventsOverTimeQuery = ({
sourceConfiguration: {
fields: { timestamp },
},
}: RequestBasicOptions) => {
stackByField = 'event.action',
}: MatrixHistogramRequestOptions) => {
const filter = [
...createQueryFilterClauses(filterQuery),
{
@ -44,7 +45,7 @@ export const buildEventsOverTimeQuery = ({
return {
eventActionGroup: {
terms: {
field: 'event.action',
field: stackByField,
missing: 'All others',
order: {
_count: 'desc',

View file

@ -17,6 +17,7 @@ import {
SortField,
SourceConfiguration,
TimerangeInput,
Maybe,
} from '../../graphql/types';
import { RequestFacade } from '../../types';
@ -130,6 +131,10 @@ export interface RequestBasicOptions {
defaultIndex: string[];
}
export interface MatrixHistogramRequestOptions extends RequestBasicOptions {
stackByField?: Maybe<string>;
}
export interface RequestOptions extends RequestBasicOptions {
pagination: PaginationInput;
fields: readonly string[];

View file

@ -41,6 +41,7 @@ export interface NetworkHttpRequestOptions extends RequestOptionsPaginated {
export interface NetworkDnsRequestOptions extends RequestOptionsPaginated {
isPtrIncluded: boolean;
networkDnsSortField: NetworkDnsSortField;
stackByField?: Maybe<string>;
}
export class Network {

View file

@ -64,6 +64,7 @@ export const buildDnsQuery = ({
sourceConfiguration: {
fields: { timestamp },
},
stackByField = 'dns.question.registered_domain',
timerange: { from, to },
}: NetworkDnsRequestOptions) => {
const filter = [
@ -87,7 +88,7 @@ export const buildDnsQuery = ({
...getCountAgg(),
dns_name_query_count: {
terms: {
field: 'dns.question.registered_domain',
field: stackByField,
size: querySize,
order: {
...getQueryOrder(networkDnsSortField),

View file

@ -12,6 +12,7 @@ import {
} from '../../graphql/types';
import { FrameworkRequest, RequestOptionsPaginated } from '../framework';
import { TotalValue } from '../types';
import { NetworkDnsRequestOptions } from '.';
export interface NetworkAdapter {
getNetworkTopCountries(
@ -22,7 +23,7 @@ export interface NetworkAdapter {
req: FrameworkRequest,
options: RequestOptionsPaginated
): Promise<NetworkTopNFlowData>;
getNetworkDns(req: FrameworkRequest, options: RequestOptionsPaginated): Promise<NetworkDnsData>;
getNetworkDns(req: FrameworkRequest, options: NetworkDnsRequestOptions): Promise<NetworkDnsData>;
getNetworkHttp(req: FrameworkRequest, options: RequestOptionsPaginated): Promise<NetworkHttpData>;
}

View file

@ -11000,8 +11000,6 @@
"xpack.siem.auditd.violatedSeLinuxPolicyDescription": "selinuxポリシーに違反しました",
"xpack.siem.auditd.wasAuthorizedToUseDescription": "が以下の使用を承認されました:",
"xpack.siem.auditd.withResultDescription": "結果付き",
"xpack.siem.authenticationsOverTime.authenticationCountTitle": "認証カウント",
"xpack.siem.authenticationsOverTime.unit": "{totalCount, plural, =1 {authentication} other {authentications}}",
"xpack.siem.authenticationsTable.authenticationFailures": "認証",
"xpack.siem.authenticationsTable.failures": "失敗",
"xpack.siem.authenticationsTable.lastFailedDestination": "前回失敗したデスティネーション",
@ -11135,9 +11133,6 @@
"xpack.siem.eventDetails.table": "表",
"xpack.siem.eventDetails.toggleColumnTooltip": "列を切り替えます",
"xpack.siem.eventDetails.value": "値",
"xpack.siem.eventsOverTime.eventCountFrequencyByActionTitle": "アクション別のイベントカウント",
"xpack.siem.eventsOverTime.showing": "表示中",
"xpack.siem.eventsOverTime.unit": "{totalCount, plural, =1 {event} other {events}}",
"xpack.siem.eventsViewer.eventsLabel": "イベント",
"xpack.siem.eventsViewer.showingLabel": "表示中",
"xpack.siem.eventsViewer.unit": "{totalCount, plural, =1 {event} other {events}}",
@ -13306,4 +13301,4 @@
"xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。",
"xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。"
}
}
}

View file

@ -10999,8 +10999,6 @@
"xpack.siem.auditd.violatedSeLinuxPolicyDescription": "已违反 selinux 策略",
"xpack.siem.auditd.wasAuthorizedToUseDescription": "有权使用",
"xpack.siem.auditd.withResultDescription": ",结果为",
"xpack.siem.authenticationsOverTime.authenticationCountTitle": "身份验证计数",
"xpack.siem.authenticationsOverTime.unit": "{totalCount, plural, =1 {个身份验证} other {个身份验证}}",
"xpack.siem.authenticationsTable.authenticationFailures": "身份验证",
"xpack.siem.authenticationsTable.failures": "失败",
"xpack.siem.authenticationsTable.lastFailedDestination": "上一失败目标",
@ -11134,9 +11132,6 @@
"xpack.siem.eventDetails.table": "表",
"xpack.siem.eventDetails.toggleColumnTooltip": "切换列",
"xpack.siem.eventDetails.value": "值",
"xpack.siem.eventsOverTime.eventCountFrequencyByActionTitle": "事件计数 - 按操作",
"xpack.siem.eventsOverTime.showing": "显示",
"xpack.siem.eventsOverTime.unit": "{totalCount, plural, =1 {个事件} other {个事件}}",
"xpack.siem.eventsViewer.eventsLabel": "事件",
"xpack.siem.eventsViewer.showingLabel": "显示",
"xpack.siem.eventsViewer.unit": "{totalCount, plural, =1 {个事件} other {个事件}}",
@ -13305,4 +13300,4 @@
"xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。",
"xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。"
}
}
}

View file

@ -1,98 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import expect from '@kbn/expect';
import { EventsOverTimeGqlQuery } from '../../../../legacy/plugins/siem/public/containers/events/events_over_time/events_over_time.gql_query';
import { GetEventsOverTimeQuery } from '../../../../legacy/plugins/siem/public/graphql/types';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const client = getService('siemGraphQLClient');
const FROM = new Date('2000-01-01T00:00:00.000Z').valueOf();
const TO = new Date('3000-01-01T00:00:00.000Z').valueOf();
describe('Events over time', () => {
describe('With filebeat', () => {
before(() => esArchiver.load('filebeat/default'));
after(() => esArchiver.unload('filebeat/default'));
it('Make sure that we get events over time data', () => {
return client
.query<GetEventsOverTimeQuery.Query>({
query: EventsOverTimeGqlQuery,
variables: {
sourceId: 'default',
timerange: {
interval: '12h',
to: TO,
from: FROM,
},
defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],
inspect: false,
},
})
.then(resp => {
const expectedData = [
{
x: new Date('2018-12-20T00:00:00.000Z').valueOf(),
y: 4884,
g: 'All others',
__typename: 'MatrixOverTimeHistogramData',
},
{
x: new Date('2018-12-20T00:00:00.000Z').valueOf(),
y: 1273,
g: 'netflow_flow',
__typename: 'MatrixOverTimeHistogramData',
},
];
const eventsOverTime = resp.data.source.EventsOverTime;
expect(eventsOverTime.eventsOverTime).to.eql(expectedData);
});
});
});
describe('With packetbeat', () => {
before(() => esArchiver.load('packetbeat/default'));
after(() => esArchiver.unload('packetbeat/default'));
it('Make sure that we get events over time data', () => {
return client
.query<GetEventsOverTimeQuery.Query>({
query: EventsOverTimeGqlQuery,
variables: {
sourceId: 'default',
timerange: {
interval: '12h',
to: TO,
from: FROM,
},
defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],
inspect: false,
},
})
.then(resp => {
const expectedData = [
{
x: new Date('2018-12-20T00:00:00.000Z').valueOf(),
y: 4884,
g: 'All others',
__typename: 'MatrixOverTimeHistogramData',
},
{
x: new Date('2018-12-20T00:00:00.000Z').valueOf(),
y: 1273,
g: 'netflow_flow',
__typename: 'MatrixOverTimeHistogramData',
},
];
const eventsOverTime = resp.data.source.EventsOverTime;
expect(eventsOverTime.eventsOverTime).to.eql(expectedData);
});
});
});
});
}

View file

@ -7,7 +7,6 @@
export default function({ loadTestFile }) {
describe('Siem GraphQL Endpoints', () => {
loadTestFile(require.resolve('./authentications'));
loadTestFile(require.resolve('./events_over_time'));
loadTestFile(require.resolve('./hosts'));
loadTestFile(require.resolve('./kpi_network'));
loadTestFile(require.resolve('./kpi_hosts'));

View file

@ -29,22 +29,24 @@ export default function({ getService }: FtrProviderContext) {
.query<GetNetworkDnsQuery.Query>({
query: networkDnsQuery,
variables: {
sourceId: 'default',
timerange: {
interval: '12h',
to: TO,
from: FROM,
},
defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],
isDNSHistogram: false,
inspect: false,
isPtrIncluded: false,
sort: { field: NetworkDnsFields.uniqueDomains, direction: Direction.asc },
pagination: {
activePage: 0,
cursorStart: 0,
fakePossibleCount: 30,
querySize: 10,
},
defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],
inspect: false,
sort: { field: NetworkDnsFields.uniqueDomains, direction: Direction.asc },
sourceId: 'default',
stackByField: 'dns.question.registered_domain',
timerange: {
interval: '12h',
to: TO,
from: FROM,
},
},
})
.then(resp => {
@ -63,22 +65,24 @@ export default function({ getService }: FtrProviderContext) {
.query<GetNetworkDnsQuery.Query>({
query: networkDnsQuery,
variables: {
sourceId: 'default',
timerange: {
interval: '12h',
to: TO,
from: FROM,
},
defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],
isDNSHistogram: false,
inspect: false,
isPtrIncluded: false,
sort: { field: NetworkDnsFields.uniqueDomains, direction: Direction.desc },
pagination: {
activePage: 0,
cursorStart: 0,
fakePossibleCount: 30,
querySize: 10,
},
defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],
inspect: false,
sourceId: 'default',
sort: { field: NetworkDnsFields.uniqueDomains, direction: Direction.desc },
stackByField: 'dns.question.registered_domain',
timerange: {
interval: '12h',
to: TO,
from: FROM,
},
},
})
.then(resp => {