[SIEM] Add DNS histogram (#50409) (#51474)

* add histogram

* fix types error

* rename matrix histogram component

* clean up

* add dns histogram container

* wrap passed in function with useCallback

* isolate utils functions

* extract types

* disable chart legend for dns histogram

* add dns bytes in
This commit is contained in:
Angela Chuang 2019-11-22 18:18:28 +00:00 committed by GitHub
parent 3c1695a200
commit bb79547905
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 564 additions and 155 deletions

View file

@ -7,7 +7,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { MatrixOverTimeHistogram } from '.';
import { MatrixHistogram } from '.';
jest.mock('@elastic/eui', () => {
return {
@ -53,7 +53,7 @@ describe('Load More Events Table Component', () => {
};
describe('rendering', () => {
test('it renders EuiLoadingContent on initialLoad', () => {
const wrapper = shallow(<MatrixOverTimeHistogram {...mockMatrixOverTimeHistogramProps} />);
const wrapper = shallow(<MatrixHistogram {...mockMatrixOverTimeHistogramProps} />);
expect(wrapper.find(`[data-test-subj="initialLoadingPanelMatrixOverTime"]`)).toBeTruthy();
});
@ -65,7 +65,7 @@ describe('Load More Events Table Component', () => {
totalCount: 10,
loading: true,
};
const wrapper = shallow(<MatrixOverTimeHistogram {...mockProps} />);
const wrapper = shallow(<MatrixHistogram {...mockProps} />);
expect(wrapper.find('.loader')).toBeTruthy();
});
@ -76,7 +76,7 @@ describe('Load More Events Table Component', () => {
totalCount: 10,
loading: false,
};
const wrapper = shallow(<MatrixOverTimeHistogram {...mockProps} />);
const wrapper = shallow(<MatrixHistogram {...mockProps} />);
expect(wrapper.find(`.barchart`)).toBeTruthy();
});

View file

@ -5,100 +5,50 @@
*/
import React, { useState, useEffect } from 'react';
import { ScaleType, niceTimeFormatter, Position } from '@elastic/charts';
import { ScaleType } from '@elastic/charts';
import { getOr, head, last } from 'lodash/fp';
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 { BarChart } from '../charts/barchart';
import { HeaderSection } from '../header_section';
import { ChartSeriesData, UpdateDateRange } from '../charts/common';
import { MatrixOverTimeHistogramData } from '../../graphql/types';
import { ChartSeriesData } from '../charts/common';
import { DEFAULT_DARK_MODE } from '../../../common/constants';
import { useKibanaUiSetting } from '../../lib/settings/use_kibana_ui_setting';
import { Loader } from '../loader';
import { Panel } from '../panel';
import { getBarchartConfigs, getCustomChartData } from './utils';
import { MatrixHistogramProps, MatrixHistogramDataTypes } from './types';
export interface MatrixOverTimeBasicProps {
id: string;
data: MatrixOverTimeHistogramData[];
loading: boolean;
startDate: number;
endDate: number;
updateDateRange: UpdateDateRange;
totalCount: number;
}
export interface MatrixOverTimeProps extends MatrixOverTimeBasicProps {
customChartData?: ChartSeriesData[];
title: string;
subtitle?: string;
dataKey: string;
}
const getBarchartConfigs = (from: number, to: number, onBrushEnd: UpdateDateRange) => ({
series: {
xScaleType: ScaleType.Time,
yScaleType: ScaleType.Linear,
stackAccessors: ['g'],
},
axis: {
xTickFormatter: niceTimeFormatter([from, to]),
yTickFormatter: (value: string | number): string => value.toLocaleString(),
tickSize: 8,
},
settings: {
legendPosition: Position.Bottom,
onBrushEnd,
showLegend: true,
theme: {
scales: {
barsPadding: 0.08,
},
chartMargins: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
chartPaddings: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
},
},
customHeight: 324,
});
export const MatrixOverTimeHistogram = ({
customChartData,
id,
loading,
export const MatrixHistogram = ({
data,
dataKey,
endDate,
updateDateRange,
id,
loading,
mapping,
scaleType = ScaleType.Time,
startDate,
title,
subtitle,
title,
totalCount,
}: MatrixOverTimeProps) => {
const bucketStartDate = getOr(startDate, 'x', head(data));
const bucketEndDate = getOr(endDate, 'x', last(data));
const barchartConfigs = getBarchartConfigs(bucketStartDate!, bucketEndDate!, updateDateRange);
updateDateRange,
yTickFormatter,
showLegend,
}: MatrixHistogramProps<MatrixHistogramDataTypes>) => {
const barchartConfigs = getBarchartConfigs({
from: startDate,
to: endDate,
onBrushEnd: updateDateRange,
scaleType,
yTickFormatter,
showLegend,
});
const [showInspect, setShowInspect] = useState(false);
const [darkMode] = useKibanaUiSetting(DEFAULT_DARK_MODE);
const [loadingInitial, setLoadingInitial] = useState(false);
const barChartData: ChartSeriesData[] = customChartData || [
{
key: dataKey,
value: data,
},
];
const barChartData: ChartSeriesData[] = getCustomChartData(data, mapping);
useEffect(() => {
if (totalCount >= 0 && loadingInitial) {

View file

@ -0,0 +1,32 @@
/*
* 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 { ScaleType } from '@elastic/charts';
import { MatrixOverTimeHistogramData, MatrixOverOrdinalHistogramData } from '../../graphql/types';
import { AuthMatrixDataFields } from '../page/hosts/authentications_over_time/utils';
import { UpdateDateRange } from '../charts/common';
export type MatrixHistogramDataTypes = MatrixOverTimeHistogramData | MatrixOverOrdinalHistogramData;
export type MatrixHistogramMappingTypes = AuthMatrixDataFields;
export interface MatrixHistogramBasicProps<T> {
data: T[];
endDate: number;
id: string;
loading: boolean;
mapping?: MatrixHistogramMappingTypes;
startDate: number;
totalCount: number;
updateDateRange: UpdateDateRange;
}
export interface MatrixHistogramProps<T> extends MatrixHistogramBasicProps<T> {
dataKey?: string;
scaleType?: ScaleType;
subtitle?: string;
title?: string;
yTickFormatter?: (value: number) => string;
showLegend?: boolean;
}

View file

@ -0,0 +1,93 @@
/*
* 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 { ScaleType, niceTimeFormatter, Position } from '@elastic/charts';
import { get, groupBy, map, toPairs } from 'lodash/fp';
import numeral from '@elastic/numeral';
import { UpdateDateRange, ChartSeriesData } from '../charts/common';
import { MatrixHistogramDataTypes, MatrixHistogramMappingTypes } from './types';
export const getBarchartConfigs = ({
from,
to,
scaleType,
onBrushEnd,
yTickFormatter,
showLegend,
}: {
from: number;
to: number;
scaleType: ScaleType;
onBrushEnd: UpdateDateRange;
yTickFormatter?: (value: number) => string;
showLegend?: boolean;
}) => ({
series: {
xScaleType: scaleType || ScaleType.Time,
yScaleType: ScaleType.Linear,
stackAccessors: ['g'],
},
axis: {
xTickFormatter: scaleType === ScaleType.Time ? niceTimeFormatter([from, to]) : undefined,
yTickFormatter:
yTickFormatter != null
? yTickFormatter
: (value: string | number): string => value.toLocaleString(),
tickSize: 8,
},
settings: {
legendPosition: Position.Bottom,
onBrushEnd,
showLegend: showLegend || true,
theme: {
scales: {
barsPadding: 0.08,
},
chartMargins: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
chartPaddings: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
},
},
customHeight: 324,
});
export const formatToChartDataItem = ([key, value]: [
string,
MatrixHistogramDataTypes[]
]): ChartSeriesData => ({
key,
value,
});
export const getCustomChartData = (
data: MatrixHistogramDataTypes[],
mapping?: MatrixHistogramMappingTypes
): ChartSeriesData[] => {
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;
}, formattedChartData);
else return formattedChartData;
};
export const bytesFormatter = (value: number) => {
return numeral(value).format('0,0.[0]b');
};

View file

@ -7,21 +7,23 @@
import React from 'react';
import * as i18n from './translation';
import { getCustomChartData } from './utils';
import { MatrixOverTimeHistogram, MatrixOverTimeBasicProps } from '../../../matrix_over_time';
import { MatrixHistogram } from '../../../matrix_histogram';
import { MatrixHistogramBasicProps } from '../../../matrix_histogram/types';
import { MatrixOverTimeHistogramData } from '../../../../graphql/types';
import { authMatrixDataMappingFields } from './utils';
export const AuthenticationsOverTimeHistogram = (props: MatrixOverTimeBasicProps) => {
export const AuthenticationsOverTimeHistogram = (
props: MatrixHistogramBasicProps<MatrixOverTimeHistogramData>
) => {
const dataKey = 'authenticationsOverTime';
const { data, ...matrixOverTimeProps } = props;
const customChartData = getCustomChartData(data);
return (
<MatrixOverTimeHistogram
title={i18n.AUTHENTICATIONS_COUNT}
<MatrixHistogram
mapping={authMatrixDataMappingFields}
dataKey={dataKey}
data={data}
customChartData={customChartData}
title={i18n.AUTHENTICATIONS_COUNT}
{...matrixOverTimeProps}
/>
);

View file

@ -4,36 +4,28 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { groupBy, map, toPairs } from 'lodash/fp';
import { ChartSeriesData } from '../../../charts/common';
import { MatrixOverTimeHistogramData } from '../../../../graphql/types';
import { KpiHostsChartColors } from '../kpi_hosts/types';
const formatToChartDataItem = ([key, value]: [
string,
MatrixOverTimeHistogramData[]
]): ChartSeriesData => ({
key,
value,
});
enum AuthMatrixDataGroup {
authSuccess = 'authentication_success',
authFailure = 'authentication_failure',
}
const addCustomColors = (item: ChartSeriesData) => {
if (item.key === 'authentication_success') {
item.color = KpiHostsChartColors.authSuccess;
}
export interface AuthMatrixDataFields {
[AuthMatrixDataGroup.authSuccess]: ChartSeriesData;
[AuthMatrixDataGroup.authFailure]: ChartSeriesData;
}
if (item.key === 'authentication_failure') {
item.color = KpiHostsChartColors.authFailure;
}
return item;
};
export const getCustomChartData = (data: MatrixOverTimeHistogramData[]): ChartSeriesData[] => {
const dataGroupedByEvent = groupBy('g', data);
const dataGroupedEntries = toPairs(dataGroupedByEvent);
const formattedChartData = map(formatToChartDataItem, dataGroupedEntries);
return map(addCustomColors, formattedChartData);
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

@ -7,16 +7,20 @@
import React from 'react';
import * as i18n from './translation';
import { MatrixOverTimeHistogram, MatrixOverTimeBasicProps } from '../../../matrix_over_time';
import { MatrixHistogram } from '../../../matrix_histogram';
import { MatrixHistogramBasicProps } from '../../../matrix_histogram/types';
import { MatrixOverTimeHistogramData } from '../../../../graphql/types';
export const EventsOverTimeHistogram = (props: MatrixOverTimeBasicProps) => {
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 (
<MatrixOverTimeHistogram
<MatrixHistogram
title={i18n.EVENT_COUNT_FREQUENCY_BY_ACTION}
subtitle={subtitle}
dataKey={dataKey}

View file

@ -0,0 +1,32 @@
/*
* 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 { bytesFormatter } from '../../../matrix_histogram/utils';
import { MatrixOverOrdinalHistogramData } from '../../../../graphql/types';
import { MatrixHistogramBasicProps } from '../../../matrix_histogram/types';
export const NetworkDnsHistogram = (
props: MatrixHistogramBasicProps<MatrixOverOrdinalHistogramData>
) => {
const dataKey = 'histogram';
const { ...matrixOverTimeProps } = props;
return (
<MatrixHistogram
title={i18n.NETWORK_DNS_HISTOGRAM}
dataKey={dataKey}
scaleType={ScaleType.Ordinal}
yTickFormatter={bytesFormatter}
showLegend={false}
{...matrixOverTimeProps}
/>
);
};

View file

@ -0,0 +1,11 @@
/*
* 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 NETWORK_DNS_HISTOGRAM = i18n.translate('xpack.siem.DNS.histogramTitle', {
defaultMessage: 'Top DNS domains bytes count',
});

View file

@ -126,5 +126,57 @@ export const mockData: { NetworkDns: NetworkDnsData } = {
fakeTotalCount: 50,
showMorePagesIndicator: true,
},
histogram: [
{
x: 'nflxvideo.net',
g: 'nflxvideo.net',
y: 12546,
},
{
x: 'apple.com',
g: 'apple.com',
y: 31687,
},
{
x: 'googlevideo.com',
g: 'googlevideo.com',
y: 16292,
},
{
x: 'netflix.com',
g: 'netflix.com',
y: 218193,
},
{
x: 'samsungcloudsolution.com',
g: 'samsungcloudsolution.com',
y: 11702,
},
{
x: 'doubleclick.net',
g: 'doubleclick.net',
y: 14372,
},
{
x: 'digitalocean.com',
g: 'digitalocean.com',
y: 4111,
},
{
x: 'samsungelectronics.com',
g: 'samsungelectronics.com',
y: 36592,
},
{
x: 'google.com',
g: 'google.com',
y: 8072,
},
{
x: 'samsungcloudsolution.net',
g: 'samsungcloudsolution.net',
y: 11518,
},
],
},
};

View file

@ -50,6 +50,11 @@ export const networkDnsQuery = gql`
dsl
response
}
histogram {
x
y
g
}
}
}
}

View file

@ -16,15 +16,17 @@ import {
NetworkDnsEdges,
NetworkDnsSortField,
PageInfoPaginated,
MatrixOverOrdinalHistogramData,
} from '../../graphql/types';
import { inputsModel, networkModel, networkSelectors, State, inputsSelectors } from '../../store';
import { generateTablePaginationOptions } from '../../components/paginated_table/helpers';
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';
const ID = 'networkDnsQuery';
const HISTOGRAM_ID = 'networkDnsHistogramQuery';
export interface NetworkDnsArgs {
id: string;
inspect: inputsModel.InspectQuery;
@ -35,6 +37,7 @@ export interface NetworkDnsArgs {
pageInfo: PageInfoPaginated;
refetch: inputsModel.Refetch;
totalCount: number;
histogram: MatrixOverOrdinalHistogramData[];
}
export interface OwnProps extends QueryTemplatePaginatedProps {
@ -52,7 +55,7 @@ export interface NetworkDnsComponentReduxProps {
type NetworkDnsProps = OwnProps & NetworkDnsComponentReduxProps;
class NetworkDnsComponentQuery extends QueryTemplatePaginated<
export class NetworkDnsComponentQuery extends QueryTemplatePaginated<
NetworkDnsProps,
GetNetworkDnsQuery.Query,
GetNetworkDnsQuery.Variables
@ -129,6 +132,7 @@ class NetworkDnsComponentQuery extends QueryTemplatePaginated<
pageInfo: getOr({}, 'source.NetworkDns.pageInfo', data),
refetch: this.memoizedRefetchQuery(variables, limit, refetch),
totalCount: getOr(-1, 'source.NetworkDns.totalCount', data),
histogram: getOr(null, 'source.NetworkDns.histogram', data),
});
}}
</Query>
@ -144,6 +148,24 @@ const makeMapStateToProps = () => {
return {
...getNetworkDnsSelector(state),
isInspected,
id,
};
};
return mapStateToProps;
};
const makeMapHistogramStateToProps = () => {
const getNetworkDnsSelector = networkSelectors.dnsSelector();
const getQuery = inputsSelectors.globalQueryByIdSelector();
const mapStateToProps = (state: State, { id = HISTOGRAM_ID }: OwnProps) => {
const { isInspected } = getQuery(state, id);
return {
...getNetworkDnsSelector(state),
activePage: DEFAULT_TABLE_ACTIVE_PAGE,
limit: DEFAULT_TABLE_LIMIT,
isInspected,
id,
};
};
@ -151,3 +173,6 @@ const makeMapStateToProps = () => {
};
export const NetworkDnsQuery = connect(makeMapStateToProps)(NetworkDnsComponentQuery);
export const NetworkDnsHistogramQuery = connect(makeMapHistogramStateToProps)(
NetworkDnsComponentQuery
);

View file

@ -8034,6 +8034,26 @@
"type": { "kind": "OBJECT", "name": "Inspect", "ofType": null },
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "histogram",
"description": "",
"args": [],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "MatrixOverOrdinalHistogramData",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
@ -8135,6 +8155,53 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "MatrixOverOrdinalHistogramData",
"description": "",
"fields": [
{
"name": "x",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "y",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "g",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "NetworkHttpSortField",

View file

@ -1626,6 +1626,8 @@ export interface NetworkDnsData {
pageInfo: PageInfoPaginated;
inspect?: Maybe<Inspect>;
histogram?: Maybe<MatrixOverOrdinalHistogramData[]>;
}
export interface NetworkDnsEdges {
@ -1648,6 +1650,14 @@ export interface NetworkDnsItem {
uniqueDomains?: Maybe<number>;
}
export interface MatrixOverOrdinalHistogramData {
x: string;
y: number;
g: string;
}
export interface NetworkHttpData {
edges: NetworkHttpEdges[];
@ -3311,6 +3321,8 @@ export namespace GetNetworkDnsQuery {
pageInfo: PageInfo;
inspect: Maybe<Inspect>;
histogram: Maybe<Histogram[]>;
};
export type Edges = {
@ -3360,6 +3372,16 @@ export namespace GetNetworkDnsQuery {
response: string[];
};
export type Histogram = {
__typename?: 'MatrixOverOrdinalHistogramData';
x: string;
y: number;
g: string;
};
}
export namespace GetNetworkHttpQuery {

View file

@ -7,13 +7,16 @@
import React from 'react';
import { getOr } from 'lodash/fp';
import { EuiSpacer } from '@elastic/eui';
import { NetworkDnsTable } from '../../../components/page/network/network_dns_table';
import { NetworkDnsQuery } from '../../../containers/network_dns';
import { NetworkDnsQuery, NetworkDnsHistogramQuery } from '../../../containers/network_dns';
import { manageQuery } from '../../../components/page/manage_query';
import { DnsQueryTabBodyProps } from './types';
import { NetworkDnsHistogram } from '../../../components/page/network/dns_histogram';
const NetworkDnsTableManage = manageQuery(NetworkDnsTable);
const NetworkDnsHistogramManage = manageQuery(NetworkDnsHistogram);
export const DnsQueryTabBody = ({
to,
@ -22,42 +25,70 @@ export const DnsQueryTabBody = ({
from,
setQuery,
type,
updateDateRange = () => {},
}: DnsQueryTabBodyProps) => (
<NetworkDnsQuery
endDate={to}
filterQuery={filterQuery}
skip={isInitializing}
sourceId="default"
startDate={from}
type={type}
>
{({
totalCount,
loading,
networkDns,
pageInfo,
loadPage,
id,
inspect,
isInspected,
refetch,
}) => (
<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>
<>
<NetworkDnsHistogramQuery
endDate={to}
filterQuery={filterQuery}
skip={isInitializing}
sourceId="default"
startDate={from}
type={type}
>
{({ totalCount, loading, id, inspect, refetch, histogram }) => (
<NetworkDnsHistogramManage
id={id}
loading={loading}
data={histogram}
endDate={to}
startDate={from}
inspect={inspect}
refetch={refetch}
setQuery={setQuery}
totalCount={totalCount}
updateDateRange={updateDateRange}
/>
)}
</NetworkDnsHistogramQuery>
<EuiSpacer />
<NetworkDnsQuery
endDate={to}
filterQuery={filterQuery}
skip={isInitializing}
sourceId="default"
startDate={from}
type={type}
>
{({
totalCount,
loading,
networkDns,
pageInfo,
loadPage,
id,
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}
/>
)}
</NetworkDnsQuery>
</>
);
DnsQueryTabBody.displayName = 'DNSQueryTabBody';

View file

@ -19,6 +19,7 @@ import { DnsQueryTabBody } from './dns_query_tab_body';
import { ConditionalFlexGroup } from './conditional_flex_group';
import { NetworkRoutesProps, NetworkRouteType } from './types';
import { TlsQueryTabBody } from './tls_query_tab_body';
import { Anomaly } from '../../../components/ml/types';
export const NetworkRoutes = ({
networkPagePath,
@ -32,7 +33,7 @@ export const NetworkRoutes = ({
setAbsoluteRangeDatePicker,
}: NetworkRoutesProps) => {
const narrowDateRange = useCallback(
(score, interval) => {
(score: Anomaly, interval: string) => {
const fromTo = scoreIntervalToDateTime(score, interval);
setAbsoluteRangeDatePicker({
id: 'global',
@ -42,6 +43,12 @@ export const NetworkRoutes = ({
},
[scoreIntervalToDateTime, setAbsoluteRangeDatePicker]
);
const updateDateRange = useCallback(
(min: number, max: number) => {
setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max });
},
[from, to]
);
const tabProps = {
networkPagePath,
@ -52,6 +59,7 @@ export const NetworkRoutes = ({
from,
indexPattern,
setQuery,
updateDateRange,
};
const anomaliesProps = {

View file

@ -14,10 +14,13 @@ import { NarrowDateRange } from '../../../components/ml/types';
import { GlobalTimeArgs } from '../../../containers/global_time';
import { SetAbsoluteRangeDatePicker } from '../types';
import { UpdateDateRange } from '../../../components/charts/common';
interface QueryTabBodyProps {
type: networkModel.NetworkType;
filterQuery?: string | ESTermQuery;
updateDateRange?: UpdateDateRange;
narrowDateRange?: NarrowDateRange;
}
export type DnsQueryTabBodyProps = QueryTabBodyProps & GlobalTimeArgs;

View file

@ -145,11 +145,18 @@ export const networkSchema = gql`
cursor: CursorType!
}
type MatrixOverOrdinalHistogramData {
x: String!
y: Float!
g: String!
}
type NetworkDnsData {
edges: [NetworkDnsEdges!]!
totalCount: Float!
pageInfo: PageInfoPaginated!
inspect: Inspect
histogram: [MatrixOverOrdinalHistogramData!]
}
enum NetworkHttpFields {

View file

@ -1628,6 +1628,8 @@ export interface NetworkDnsData {
pageInfo: PageInfoPaginated;
inspect?: Maybe<Inspect>;
histogram?: Maybe<MatrixOverOrdinalHistogramData[]>;
}
export interface NetworkDnsEdges {
@ -1650,6 +1652,14 @@ export interface NetworkDnsItem {
uniqueDomains?: Maybe<number>;
}
export interface MatrixOverOrdinalHistogramData {
x: string;
y: number;
g: string;
}
export interface NetworkHttpData {
edges: NetworkHttpEdges[];
@ -6949,6 +6959,8 @@ export namespace NetworkDnsDataResolvers {
pageInfo?: PageInfoResolver<PageInfoPaginated, TypeParent, TContext>;
inspect?: InspectResolver<Maybe<Inspect>, TypeParent, TContext>;
histogram?: HistogramResolver<Maybe<MatrixOverOrdinalHistogramData[]>, TypeParent, TContext>;
}
export type EdgesResolver<
@ -6971,6 +6983,11 @@ export namespace NetworkDnsDataResolvers {
Parent = NetworkDnsData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type HistogramResolver<
R = Maybe<MatrixOverOrdinalHistogramData[]>,
Parent = NetworkDnsData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
}
export namespace NetworkDnsEdgesResolvers {
@ -7039,6 +7056,32 @@ export namespace NetworkDnsItemResolvers {
> = Resolver<R, Parent, TContext>;
}
export namespace MatrixOverOrdinalHistogramDataResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = MatrixOverOrdinalHistogramData> {
x?: XResolver<string, TypeParent, TContext>;
y?: YResolver<number, TypeParent, TContext>;
g?: GResolver<string, TypeParent, TContext>;
}
export type XResolver<
R = string,
Parent = MatrixOverOrdinalHistogramData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type YResolver<
R = number,
Parent = MatrixOverOrdinalHistogramData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type GResolver<
R = string,
Parent = MatrixOverOrdinalHistogramData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
}
export namespace NetworkHttpDataResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = NetworkHttpData> {
edges?: EdgesResolver<NetworkHttpEdges[], TypeParent, TContext>;
@ -8704,6 +8747,7 @@ export type IResolvers<TContext = SiemContext> = {
NetworkDnsData?: NetworkDnsDataResolvers.Resolvers<TContext>;
NetworkDnsEdges?: NetworkDnsEdgesResolvers.Resolvers<TContext>;
NetworkDnsItem?: NetworkDnsItemResolvers.Resolvers<TContext>;
MatrixOverOrdinalHistogramData?: MatrixOverOrdinalHistogramDataResolvers.Resolvers<TContext>;
NetworkHttpData?: NetworkHttpDataResolvers.Resolvers<TContext>;
NetworkHttpEdges?: NetworkHttpEdgesResolvers.Resolvers<TContext>;
NetworkHttpItem?: NetworkHttpItemResolvers.Resolvers<TContext>;

View file

@ -18,6 +18,7 @@ import {
NetworkHttpData,
NetworkHttpEdges,
NetworkTopNFlowEdges,
MatrixOverOrdinalHistogramData,
} from '../../graphql/types';
import { inspectStringifyObject } from '../../utils/build_query';
import { DatabaseSearchResponse, FrameworkAdapter, FrameworkRequest } from '../framework';
@ -140,6 +141,7 @@ export class ElasticsearchNetworkAdapter implements NetworkAdapter {
);
const fakeTotalCount = fakePossibleCount <= totalCount ? fakePossibleCount : totalCount;
const edges = networkDnsEdges.splice(cursorStart, querySize - cursorStart);
const histogram = getHistogramData(edges);
const inspect = {
dsl: [inspectStringifyObject(dsl)],
response: [inspectStringifyObject(response)],
@ -154,6 +156,7 @@ export class ElasticsearchNetworkAdapter implements NetworkAdapter {
showMorePagesIndicator,
},
totalCount,
histogram,
};
}
@ -194,6 +197,32 @@ export class ElasticsearchNetworkAdapter implements NetworkAdapter {
}
}
const getHistogramData = (
data: NetworkDnsEdges[]
): MatrixOverOrdinalHistogramData[] | undefined => {
if (!Array.isArray(data)) return undefined;
return data.reduce(
(acc: MatrixOverOrdinalHistogramData[], { node: { dnsBytesOut, dnsBytesIn, _id } }) => {
if (_id != null && dnsBytesOut != null && dnsBytesIn != null)
return [
...acc,
{
x: _id,
y: dnsBytesOut,
g: 'DNS Bytes Out',
},
{
x: _id,
y: dnsBytesIn,
g: 'DNS Bytes In',
},
];
return acc;
},
[]
);
};
const getTopNFlowEdges = (
response: DatabaseSearchResponse<NetworkTopNFlowData, TermAggregation>,
options: NetworkTopNFlowRequestOptions

View file

@ -9,5 +9,5 @@
"include": [
"**/*"
],
"exclude": [],
"exclude": []
}