[SIEM] Add hosts and network anomalies histogram (#50295) (#51565)

This commit is contained in:
patrykkopycinski 2019-11-25 13:30:08 +01:00 committed by GitHub
parent 60696a08b1
commit 71a7f8a7ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 1235 additions and 352 deletions

View file

@ -83,7 +83,8 @@
"**/typescript": "3.7.2",
"**/graphql-toolkit/lodash": "^4.17.13",
"**/isomorphic-git/**/base64-js": "^1.2.1",
"**/image-diff/gm/debug": "^2.6.9"
"**/image-diff/gm/debug": "^2.6.9",
"**/deepmerge": "^4.2.2"
},
"workspaces": {
"packages": [
@ -153,6 +154,7 @@
"custom-event-polyfill": "^0.3.0",
"d3": "3.5.17",
"d3-cloud": "1.2.5",
"deepmerge": "^4.2.2",
"del": "^5.1.0",
"elasticsearch": "^16.5.0",
"elasticsearch-browser": "^16.5.0",

View file

@ -0,0 +1,30 @@
/*
* 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

@ -0,0 +1,24 @@
/*
* 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

@ -0,0 +1,37 @@
/*
* 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

@ -0,0 +1,86 @@
/*
* 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

@ -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 { 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

@ -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 React from 'react';
import { EuiSpacer } from '@elastic/eui';
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 { useKibanaUiSetting } from '../../../lib/settings/use_kibana_ui_setting';
import { DEFAULT_ANOMALY_SCORE } from '../../../../common/constants';
const AnomaliesOverTimeManage = manageQuery(AnomaliesOverTimeHistogram);
export const AnomaliesQueryTabBody = ({
endDate,
skip,
startDate,
type,
narrowDateRange,
filterQuery,
anomaliesFilterQuery,
setQuery,
hideHistogramIfEmpty,
updateDateRange = () => {},
AnomaliesTableComponent,
flowTarget,
ip,
}: AnomaliesQueryTabBodyProps) => {
const [siemJobsLoading, siemJobs] = useSiemJobs(true);
const [anomalyScore] = useKibanaUiSetting(DEFAULT_ANOMALY_SCORE);
const mergedFilterQuery = getAnomaliesFilterQuery(
filterQuery,
anomaliesFilterQuery,
siemJobs,
anomalyScore,
flowTarget,
ip
);
return (
<>
<AnomaliesOverTimeQuery
endDate={endDate}
filterQuery={mergedFilterQuery}
sourceId="default"
startDate={startDate}
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>
<AnomaliesTableComponent
startDate={startDate}
endDate={endDate}
skip={skip}
type={type as never}
narrowDateRange={narrowDateRange}
flowTarget={flowTarget}
ip={ip}
/>
</>
);
};
AnomaliesQueryTabBody.displayName = 'AnomaliesQueryTabBody';

View file

@ -0,0 +1,34 @@
/*
* 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 { ESTermQuery } from '../../../../common/typed_json';
import { NarrowDateRange } from '../../../components/ml/types';
import { UpdateDateRange } from '../../../components/charts/common';
import { SetQuery } from '../../../pages/hosts/navigation/types';
import { FlowTarget } from '../../../graphql/types';
import { HostsType } from '../../../store/hosts/model';
import { NetworkType } from '../../../store/network/model';
import { AnomaliesHostTable } from '../../../components/ml/tables/anomalies_host_table';
import { AnomaliesNetworkTable } from '../../../components/ml/tables/anomalies_network_table';
interface QueryTabBodyProps {
type: HostsType | NetworkType;
filterQuery?: string | ESTermQuery;
}
export type AnomaliesQueryTabBodyProps = QueryTabBodyProps & {
startDate: number;
endDate: number;
skip: boolean;
setQuery: SetQuery;
narrowDateRange: NarrowDateRange;
updateDateRange?: UpdateDateRange;
anomaliesFilterQuery?: object;
hideHistogramIfEmpty?: boolean;
ip?: string;
flowTarget?: FlowTarget;
AnomaliesTableComponent: typeof AnomaliesHostTable | typeof AnomaliesNetworkTable;
};

View file

@ -0,0 +1,68 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import deepmerge from 'deepmerge';
import { createFilter } from '../../helpers';
import { ESTermQuery } from '../../../../common/typed_json';
import { SiemJob } from '../../../components/ml_popover/types';
import { FlowTarget } from '../../../graphql/types';
export const getAnomaliesFilterQuery = (
filterQuery: string | ESTermQuery | undefined,
anomaliesFilterQuery: object = {},
siemJobs: SiemJob[] = [],
anomalyScore: number,
flowTarget?: FlowTarget,
ip?: string
): string => {
const siemJobIds = siemJobs
.filter(job => job.isInstalled)
.map(job => job.id)
.map(jobId => ({
match_phrase: {
job_id: jobId,
},
}));
const filterQueryString = createFilter(filterQuery);
const filterQueryObject = filterQueryString ? JSON.parse(filterQueryString) : {};
const mergedFilterQuery = deepmerge.all([
filterQueryObject,
anomaliesFilterQuery,
{
bool: {
filter: [
{
bool: {
should: siemJobIds,
minimum_should_match: 1,
},
},
{
match_phrase: {
result_type: 'record',
},
},
flowTarget &&
ip && {
match_phrase: {
[`${flowTarget}.ip`]: ip,
},
},
{
range: {
record_score: {
gte: anomalyScore,
},
},
},
],
},
},
]);
return JSON.stringify(mergedFilterQuery);
};

View file

@ -666,6 +666,53 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "AnomaliesOverTime",
"description": "",
"args": [
{
"name": "timerange",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null }
},
"defaultValue": null
},
{
"name": "filterQuery",
"description": "",
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
"defaultValue": null
},
{
"name": "defaultIndex",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
}
}
},
"defaultValue": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "OBJECT", "name": "AnomaliesOverTimeData", "ofType": null }
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "Authentications",
"description": "Gets Authentication success and failures based on a timerange",
@ -2491,6 +2538,159 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AnomaliesOverTimeData",
"description": "",
"fields": [
{
"name": "inspect",
"description": "",
"args": [],
"type": { "kind": "OBJECT", "name": "Inspect", "ofType": null },
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "anomaliesOverTime",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "MatrixOverTimeHistogramData",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "totalCount",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Inspect",
"description": "",
"fields": [
{
"name": "dsl",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "response",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "MatrixOverTimeHistogramData",
"description": "",
"fields": [
{
"name": "x",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "Float", "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": "PaginationInputPaginated",
@ -3200,57 +3400,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Inspect",
"description": "",
"fields": [
{
"name": "dsl",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "response",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AuthenticationsOverTimeData",
@ -3306,53 +3455,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "MatrixOverTimeHistogramData",
"description": "",
"fields": [
{
"name": "x",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "Float", "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": "PaginationInput",

View file

@ -456,6 +456,8 @@ export interface Source {
configuration: SourceConfiguration;
/** The status of the source */
status: SourceStatus;
AnomaliesOverTime: AnomaliesOverTimeData;
/** Gets Authentication success and failures based on a timerange */
Authentications: AuthenticationsData;
@ -556,6 +558,28 @@ export interface IndexField {
format?: Maybe<string>;
}
export interface AnomaliesOverTimeData {
inspect?: Maybe<Inspect>;
anomaliesOverTime: MatrixOverTimeHistogramData[];
totalCount: number;
}
export interface Inspect {
dsl: string[];
response: string[];
}
export interface MatrixOverTimeHistogramData {
x: number;
y: number;
g: string;
}
export interface AuthenticationsData {
edges: AuthenticationsEdges[];
@ -690,12 +714,6 @@ export interface PageInfoPaginated {
showMorePagesIndicator: boolean;
}
export interface Inspect {
dsl: string[];
response: string[];
}
export interface AuthenticationsOverTimeData {
inspect?: Maybe<Inspect>;
@ -704,14 +722,6 @@ export interface AuthenticationsOverTimeData {
totalCount: number;
}
export interface MatrixOverTimeHistogramData {
x: number;
y: number;
g: string;
}
export interface TimelineData {
edges: TimelineEdges[];
@ -2127,6 +2137,13 @@ export interface GetAllTimelineQueryArgs {
onlyUserFavorite?: Maybe<boolean>;
}
export interface AnomaliesOverTimeSourceArgs {
timerange: TimerangeInput;
filterQuery?: Maybe<string>;
defaultIndex: string[];
}
export interface AuthenticationsSourceArgs {
timerange: TimerangeInput;
@ -2421,6 +2438,58 @@ export interface DeleteTimelineMutationArgs {
// Documents
// ====================================================
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;

View file

@ -10,6 +10,8 @@ import { Route, Switch } from 'react-router-dom';
import { scoreIntervalToDateTime } from '../../../components/ml/score/score_interval_to_datetime';
import { Anomaly } from '../../../components/ml/types';
import { HostsTableType } from '../../../store/hosts/model';
import { AnomaliesQueryTabBody } from '../../../containers/anomalies/anomalies_query_tab_body';
import { AnomaliesHostTable } from '../../../components/ml/tables/anomalies_host_table';
import { HostDetailsTabsProps } from './types';
import { type } from './utils';
@ -18,7 +20,6 @@ import {
HostsQueryTabBody,
AuthenticationsQueryTabBody,
UncommonProcessQueryTabBody,
AnomaliesQueryTabBody,
EventsQueryTabBody,
} from '../navigation';
@ -84,7 +85,9 @@ const HostDetailsTabs = React.memo<HostDetailsTabsProps>(
/>
<Route
path={`${hostDetailsPagePath}/:tabName(${HostsTableType.anomalies})`}
render={() => <AnomaliesQueryTabBody {...tabProps} />}
render={() => (
<AnomaliesQueryTabBody {...tabProps} AnomaliesTableComponent={AnomaliesHostTable} />
)}
/>
<Route
path={`${hostDetailsPagePath}/:tabName(${HostsTableType.events})`}

View file

@ -11,12 +11,13 @@ import { HostsTabsProps } from './types';
import { scoreIntervalToDateTime } from '../../components/ml/score/score_interval_to_datetime';
import { Anomaly } from '../../components/ml/types';
import { HostsTableType } from '../../store/hosts/model';
import { AnomaliesQueryTabBody } from '../../containers/anomalies/anomalies_query_tab_body';
import { AnomaliesHostTable } from '../../components/ml/tables/anomalies_host_table';
import {
HostsQueryTabBody,
AuthenticationsQueryTabBody,
UncommonProcessQueryTabBody,
AnomaliesQueryTabBody,
EventsQueryTabBody,
} from './navigation';
@ -71,7 +72,9 @@ const HostsTabs = memo<HostsTabsProps>(
/>
<Route
path={`${hostsPagePath}/:tabName(${HostsTableType.anomalies})`}
render={() => <AnomaliesQueryTabBody {...tabProps} />}
render={() => (
<AnomaliesQueryTabBody {...tabProps} AnomaliesTableComponent={AnomaliesHostTable} />
)}
/>
<Route
path={`${hostsPagePath}/:tabName(${HostsTableType.events})`}

View file

@ -1,29 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { AnomaliesQueryTabBodyProps } from './types';
import { AnomaliesHostTable } from '../../../components/ml/tables/anomalies_host_table';
export const AnomaliesQueryTabBody = ({
endDate,
skip,
startDate,
type,
narrowDateRange,
hostName,
}: AnomaliesQueryTabBodyProps) => (
<AnomaliesHostTable
startDate={startDate}
endDate={endDate}
skip={skip}
type={type}
hostName={hostName}
narrowDateRange={narrowDateRange}
/>
);
AnomaliesQueryTabBody.displayName = 'AnomaliesQueryTabBody';

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
export * from './anomalies_query_tab_body';
export * from './authentications_query_tab_body';
export * from './events_query_tab_body';
export * from './hosts_query_tab_body';

View file

@ -25,6 +25,18 @@ type KeyHostsNavTab = KeyHostsNavTabWithoutMlPermission | KeyHostsNavTabWithMlPe
export type HostsNavTab = Record<KeyHostsNavTab, NavTab>;
export type SetQuery = ({
id,
inspect,
loading,
refetch,
}: {
id: string;
inspect: InspectQuery | null;
loading: boolean;
refetch: Refetch;
}) => void;
interface QueryTabBodyProps {
type: hostsModel.HostsType;
startDate: number;
@ -32,30 +44,13 @@ interface QueryTabBodyProps {
filterQuery?: string | ESTermQuery;
}
export type AnomaliesQueryTabBodyProps = QueryTabBodyProps & {
skip: boolean;
narrowDateRange: NarrowDateRange;
hostName?: string;
};
export type HostsComponentsQueryProps = QueryTabBodyProps & {
deleteQuery?: ({ id }: { id: string }) => void;
indexPattern: StaticIndexPattern;
skip: boolean;
setQuery: ({
id,
inspect,
loading,
refetch,
}: {
id: string;
inspect: InspectQuery | null;
loading: boolean;
refetch: Refetch;
}) => void;
setQuery: SetQuery;
updateDateRange?: UpdateDateRange;
narrowDateRange?: NarrowDateRange;
};
export type CommonChildren = (args: HostsComponentsQueryProps) => JSX.Element;
export type AnomaliesChildren = (args: AnomaliesQueryTabBodyProps) => JSX.Element;

View file

@ -39,6 +39,7 @@ import { NetworkTopNFlowQueryTable } from './network_top_n_flow_query_table';
import { TlsQueryTable } from './tls_query_table';
import { IPDetailsComponentProps } from './types';
import { UsersQueryTable } from './users_query_table';
import { AnomaliesQueryTabBody } from '../../../containers/anomalies/anomalies_query_tab_body';
import { esQuery } from '../../../../../../../../src/plugins/data/public';
export { getBreadcrumbs } from './utils';
@ -58,6 +59,7 @@ export const IPDetailsComponent = React.memo<IPDetailsComponentProps>(
setQuery,
to,
}) => {
const type = networkModel.NetworkType.details;
const narrowDateRange = useCallback(
(score, interval) => {
const fromTo = scoreIntervalToDateTime(score, interval);
@ -108,7 +110,7 @@ export const IPDetailsComponent = React.memo<IPDetailsComponentProps>(
skip={isInitializing}
sourceId="default"
filterQuery={filterQuery}
type={networkModel.NetworkType.details}
type={type}
ip={ip}
>
{({ id, inspect, ipOverviewData, loading, refetch }) => (
@ -127,7 +129,7 @@ export const IPDetailsComponent = React.memo<IPDetailsComponentProps>(
anomaliesData={anomaliesData}
loading={loading}
isLoadingAnomaliesData={isLoadingAnomaliesData}
type={networkModel.NetworkType.details}
type={type}
flowTarget={flowTarget}
refetch={refetch}
setQuery={setQuery}
@ -158,7 +160,7 @@ export const IPDetailsComponent = React.memo<IPDetailsComponentProps>(
ip={ip}
skip={isInitializing}
startDate={from}
type={networkModel.NetworkType.details}
type={type}
setQuery={setQuery}
indexPattern={indexPattern}
/>
@ -172,7 +174,7 @@ export const IPDetailsComponent = React.memo<IPDetailsComponentProps>(
ip={ip}
skip={isInitializing}
startDate={from}
type={networkModel.NetworkType.details}
type={type}
setQuery={setQuery}
indexPattern={indexPattern}
/>
@ -190,7 +192,7 @@ export const IPDetailsComponent = React.memo<IPDetailsComponentProps>(
ip={ip}
skip={isInitializing}
startDate={from}
type={networkModel.NetworkType.details}
type={type}
setQuery={setQuery}
indexPattern={indexPattern}
/>
@ -204,7 +206,7 @@ export const IPDetailsComponent = React.memo<IPDetailsComponentProps>(
ip={ip}
skip={isInitializing}
startDate={from}
type={networkModel.NetworkType.details}
type={type}
setQuery={setQuery}
indexPattern={indexPattern}
/>
@ -220,7 +222,7 @@ export const IPDetailsComponent = React.memo<IPDetailsComponentProps>(
ip={ip}
skip={isInitializing}
startDate={from}
type={networkModel.NetworkType.details}
type={type}
setQuery={setQuery}
/>
@ -232,7 +234,7 @@ export const IPDetailsComponent = React.memo<IPDetailsComponentProps>(
ip={ip}
skip={isInitializing}
startDate={from}
type={networkModel.NetworkType.details}
type={type}
setQuery={setQuery}
/>
@ -246,19 +248,23 @@ export const IPDetailsComponent = React.memo<IPDetailsComponentProps>(
setQuery={setQuery}
skip={isInitializing}
startDate={from}
type={networkModel.NetworkType.details}
type={type}
/>
<EuiSpacer />
<AnomaliesNetworkTable
<AnomaliesQueryTabBody
filterQuery={filterQuery}
setQuery={setQuery}
startDate={from}
endDate={to}
skip={isInitializing}
ip={ip}
type={networkModel.NetworkType.details}
type={type}
flowTarget={flowTarget}
narrowDateRange={narrowDateRange}
hideHistogramIfEmpty={true}
AnomaliesTableComponent={AnomaliesNetworkTable}
/>
</WrapperPage>
</StickyContainer>

View file

@ -1,27 +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 { AnomaliesQueryTabBodyProps } from './types';
import { AnomaliesNetworkTable } from '../../../components/ml/tables/anomalies_network_table';
export const AnomaliesQueryTabBody = ({
to,
isInitializing,
from,
type,
narrowDateRange,
}: AnomaliesQueryTabBodyProps) => (
<AnomaliesNetworkTable
startDate={from}
endDate={to}
skip={isInitializing}
type={type}
narrowDateRange={narrowDateRange}
/>
);
AnomaliesQueryTabBody.displayName = 'AnomaliesQueryTabBody';

View file

@ -17,21 +17,21 @@ import { IPsQueryTabBodyProps as CountriesQueryTabBodyProps } from './types';
const NetworkTopCountriesTableManage = manageQuery(NetworkTopCountriesTable);
export const CountriesQueryTabBody = ({
to,
endDate,
filterQuery,
isInitializing,
from,
skip,
startDate,
setQuery,
indexPattern,
flowTarget,
}: CountriesQueryTabBodyProps) => (
<NetworkTopCountriesQuery
endDate={to}
endDate={endDate}
flowTarget={flowTarget}
filterQuery={filterQuery}
skip={isInitializing}
skip={skip}
sourceId="default"
startDate={from}
startDate={startDate}
type={networkModel.NetworkType.page}
>
{({

View file

@ -12,28 +12,28 @@ import { NetworkDnsTable } from '../../../components/page/network/network_dns_ta
import { NetworkDnsQuery, NetworkDnsHistogramQuery } from '../../../containers/network_dns';
import { manageQuery } from '../../../components/page/manage_query';
import { DnsQueryTabBodyProps } from './types';
import { NetworkComponentQueryProps } from './types';
import { NetworkDnsHistogram } from '../../../components/page/network/dns_histogram';
const NetworkDnsTableManage = manageQuery(NetworkDnsTable);
const NetworkDnsHistogramManage = manageQuery(NetworkDnsHistogram);
export const DnsQueryTabBody = ({
to,
endDate,
filterQuery,
isInitializing,
from,
skip,
startDate,
setQuery,
type,
updateDateRange = () => {},
}: DnsQueryTabBodyProps) => (
}: NetworkComponentQueryProps) => (
<>
<NetworkDnsHistogramQuery
endDate={to}
endDate={endDate}
filterQuery={filterQuery}
skip={isInitializing}
skip={skip}
sourceId="default"
startDate={from}
startDate={startDate}
type={type}
>
{({ totalCount, loading, id, inspect, refetch, histogram }) => (
@ -41,8 +41,8 @@ export const DnsQueryTabBody = ({
id={id}
loading={loading}
data={histogram}
endDate={to}
startDate={from}
endDate={endDate}
startDate={startDate}
inspect={inspect}
refetch={refetch}
setQuery={setQuery}
@ -53,11 +53,11 @@ export const DnsQueryTabBody = ({
</NetworkDnsHistogramQuery>
<EuiSpacer />
<NetworkDnsQuery
endDate={to}
endDate={endDate}
filterQuery={filterQuery}
skip={isInitializing}
skip={skip}
sourceId="default"
startDate={from}
startDate={startDate}
type={type}
>
{({

View file

@ -17,18 +17,18 @@ import { HttpQueryTabBodyProps } from './types';
const NetworkHttpTableManage = manageQuery(NetworkHttpTable);
export const HttpQueryTabBody = ({
to,
endDate,
filterQuery,
isInitializing,
from,
skip,
startDate,
setQuery,
}: HttpQueryTabBodyProps) => (
<NetworkHttpQuery
endDate={to}
endDate={endDate}
filterQuery={filterQuery}
skip={isInitializing}
skip={skip}
sourceId="default"
startDate={from}
startDate={startDate}
type={networkModel.NetworkType.page}
>
{({

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
export * from './anomalies_query_tab_body';
export * from './network_routes';
export * from './network_routes_loading';
export * from './nav_tabs';

View file

@ -17,21 +17,21 @@ import { IPsQueryTabBodyProps } from './types';
const NetworkTopNFlowTableManage = manageQuery(NetworkTopNFlowTable);
export const IPsQueryTabBody = ({
to,
endDate,
filterQuery,
isInitializing,
from,
skip,
startDate,
setQuery,
indexPattern,
flowTarget,
}: IPsQueryTabBodyProps) => (
<NetworkTopNFlowQuery
endDate={to}
endDate={endDate}
flowTarget={flowTarget}
filterQuery={filterQuery}
skip={isInitializing}
skip={skip}
sourceId="default"
startDate={from}
startDate={startDate}
type={networkModel.NetworkType.page}
>
{({

View file

@ -14,7 +14,8 @@ import { scoreIntervalToDateTime } from '../../../components/ml/score/score_inte
import { IPsQueryTabBody } from './ips_query_tab_body';
import { CountriesQueryTabBody } from './countries_query_tab_body';
import { HttpQueryTabBody } from './http_query_tab_body';
import { AnomaliesQueryTabBody } from './anomalies_query_tab_body';
import { AnomaliesQueryTabBody } from '../../../containers/anomalies/anomalies_query_tab_body';
import { AnomaliesNetworkTable } from '../../../components/ml/tables/anomalies_network_table';
import { DnsQueryTabBody } from './dns_query_tab_body';
import { ConditionalFlexGroup } from './conditional_flex_group';
import { NetworkRoutesProps, NetworkRouteType } from './types';
@ -50,24 +51,44 @@ export const NetworkRoutes = ({
[from, to]
);
const tabProps = {
networkPagePath,
const networkAnomaliesFilterQuery = {
bool: {
should: [
{
exists: {
field: 'source.ip',
},
},
{
exists: {
field: 'destination.ip',
},
},
],
minimum_should_match: 1,
},
};
const commonProps = {
startDate: from,
endDate: to,
skip: isInitializing,
type,
to,
filterQuery,
isInitializing,
from,
indexPattern,
narrowDateRange,
setQuery,
filterQuery,
};
const tabProps = {
...commonProps,
indexPattern,
updateDateRange,
};
const anomaliesProps = {
from,
to,
isInitializing,
type,
narrowDateRange,
...commonProps,
anomaliesFilterQuery: networkAnomaliesFilterQuery,
AnomaliesTableComponent: AnomaliesNetworkTable,
};
return (
@ -115,7 +136,12 @@ export const NetworkRoutes = ({
/>
<Route
path={`${networkPagePath}/:tabName(${NetworkRouteType.anomalies})`}
render={() => <AnomaliesQueryTabBody {...anomaliesProps} />}
render={() => (
<AnomaliesQueryTabBody
{...anomaliesProps}
AnomaliesTableComponent={AnomaliesNetworkTable}
/>
)}
/>
</Switch>
);

View file

@ -13,23 +13,23 @@ import { TlsQueryTabBodyProps } from './types';
const TlsTableManage = manageQuery(TlsTable);
export const TlsQueryTabBody = ({
to,
endDate,
filterQuery,
flowTarget,
ip = '',
setQuery,
isInitializing,
from,
skip,
startDate,
type,
}: TlsQueryTabBodyProps) => (
<TlsQuery
endDate={to}
endDate={endDate}
filterQuery={filterQuery}
flowTarget={flowTarget}
ip={ip}
skip={isInitializing}
skip={skip}
sourceId="default"
startDate={from}
startDate={startDate}
type={type}
>
{({ id, inspect, isInspected, tls, totalCount, pageInfo, loading, loadPage, refetch }) => (

View file

@ -10,41 +10,37 @@ import { NavTab } from '../../../components/navigation/types';
import { FlowTargetSourceDest } from '../../../graphql/types';
import { networkModel } from '../../../store';
import { ESTermQuery } from '../../../../common/typed_json';
import { NarrowDateRange } from '../../../components/ml/types';
import { GlobalTimeArgs } from '../../../containers/global_time';
import { SetAbsoluteRangeDatePicker } from '../types';
import { UpdateDateRange } from '../../../components/charts/common';
import { NarrowDateRange } from '../../../components/ml/types';
interface QueryTabBodyProps {
interface QueryTabBodyProps extends Pick<GlobalTimeArgs, 'setQuery' | 'deleteQuery'> {
skip: boolean;
type: networkModel.NetworkType;
startDate: number;
endDate: number;
filterQuery?: string | ESTermQuery;
updateDateRange?: UpdateDateRange;
narrowDateRange?: NarrowDateRange;
}
export type DnsQueryTabBodyProps = QueryTabBodyProps & GlobalTimeArgs;
export type NetworkComponentQueryProps = QueryTabBodyProps;
export type IPsQueryTabBodyProps = QueryTabBodyProps &
GlobalTimeArgs & {
indexPattern: StaticIndexPattern;
flowTarget: FlowTargetSourceDest;
};
export type IPsQueryTabBodyProps = QueryTabBodyProps & {
indexPattern: StaticIndexPattern;
flowTarget: FlowTargetSourceDest;
};
export type TlsQueryTabBodyProps = QueryTabBodyProps &
GlobalTimeArgs & {
flowTarget: FlowTargetSourceDest;
ip?: string;
};
export type TlsQueryTabBodyProps = QueryTabBodyProps & {
flowTarget: FlowTargetSourceDest;
ip?: string;
};
export type HttpQueryTabBodyProps = QueryTabBodyProps &
GlobalTimeArgs & {
ip?: string;
};
export type AnomaliesQueryTabBodyProps = QueryTabBodyProps &
Pick<GlobalTimeArgs, 'to' | 'from' | 'isInitializing'> & {
narrowDateRange: NarrowDateRange;
};
export type HttpQueryTabBodyProps = QueryTabBodyProps & {
ip?: string;
};
export type NetworkRoutesProps = GlobalTimeArgs & {
networkPagePath: string;

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export { createAnomaliesResolvers } from './resolvers';
export { anomaliesSchema } from './schema.gql';

View file

@ -0,0 +1,38 @@
/*
* 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 { Anomalies } from '../../lib/anomalies';
import { AppResolverOf, ChildResolverOf } from '../../lib/framework';
import { createOptions } from '../../utils/build_query/create_options';
import { QuerySourceResolver } from '../sources/resolvers';
import { SourceResolvers } from '../types';
export interface AnomaliesResolversDeps {
anomalies: Anomalies;
}
type QueryAnomaliesOverTimeResolver = ChildResolverOf<
AppResolverOf<SourceResolvers.AnomaliesOverTimeResolver>,
QuerySourceResolver
>;
export const createAnomaliesResolvers = (
libs: AnomaliesResolversDeps
): {
Source: {
AnomaliesOverTime: QueryAnomaliesOverTimeResolver;
};
} => ({
Source: {
async AnomaliesOverTime(source, args, { req }, info) {
const options = {
...createOptions(source, args, info),
defaultIndex: args.defaultIndex,
};
return libs.anomalies.getAnomaliesOverTime(req, options);
},
},
});

View file

@ -0,0 +1,23 @@
/*
* 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 anomaliesSchema = gql`
type AnomaliesOverTimeData {
inspect: Inspect
anomaliesOverTime: [MatrixOverTimeHistogramData!]!
totalCount: Float!
}
extend type Source {
AnomaliesOverTime(
timerange: TimerangeInput!
filterQuery: String
defaultIndex: [String!]!
): AnomaliesOverTimeData!
}
`;

View file

@ -7,6 +7,7 @@
import { rootSchema } from '../../common/graphql/root';
import { sharedSchema } from '../../common/graphql/shared';
import { anomaliesSchema } from './anomalies';
import { authenticationsSchema } from './authentications';
import { ecsSchema } from './ecs';
import { eventsSchema } from './events';
@ -29,6 +30,7 @@ import { tlsSchema } from './tls';
import { uncommonProcessesSchema } from './uncommon_processes';
import { whoAmISchema } from './who_am_i';
export const schemas = [
anomaliesSchema,
authenticationsSchema,
ecsSchema,
eventsSchema,

View file

@ -458,6 +458,8 @@ export interface Source {
configuration: SourceConfiguration;
/** The status of the source */
status: SourceStatus;
AnomaliesOverTime: AnomaliesOverTimeData;
/** Gets Authentication success and failures based on a timerange */
Authentications: AuthenticationsData;
@ -558,6 +560,28 @@ export interface IndexField {
format?: Maybe<string>;
}
export interface AnomaliesOverTimeData {
inspect?: Maybe<Inspect>;
anomaliesOverTime: MatrixOverTimeHistogramData[];
totalCount: number;
}
export interface Inspect {
dsl: string[];
response: string[];
}
export interface MatrixOverTimeHistogramData {
x: number;
y: number;
g: string;
}
export interface AuthenticationsData {
edges: AuthenticationsEdges[];
@ -692,12 +716,6 @@ export interface PageInfoPaginated {
showMorePagesIndicator: boolean;
}
export interface Inspect {
dsl: string[];
response: string[];
}
export interface AuthenticationsOverTimeData {
inspect?: Maybe<Inspect>;
@ -706,14 +724,6 @@ export interface AuthenticationsOverTimeData {
totalCount: number;
}
export interface MatrixOverTimeHistogramData {
x: number;
y: number;
g: string;
}
export interface TimelineData {
edges: TimelineEdges[];
@ -2129,6 +2139,13 @@ export interface GetAllTimelineQueryArgs {
onlyUserFavorite?: Maybe<boolean>;
}
export interface AnomaliesOverTimeSourceArgs {
timerange: TimerangeInput;
filterQuery?: Maybe<string>;
defaultIndex: string[];
}
export interface AuthenticationsSourceArgs {
timerange: TimerangeInput;
@ -2763,6 +2780,8 @@ export namespace SourceResolvers {
configuration?: ConfigurationResolver<SourceConfiguration, TypeParent, TContext>;
/** The status of the source */
status?: StatusResolver<SourceStatus, TypeParent, TContext>;
AnomaliesOverTime?: AnomaliesOverTimeResolver<AnomaliesOverTimeData, TypeParent, TContext>;
/** Gets Authentication success and failures based on a timerange */
Authentications?: AuthenticationsResolver<AuthenticationsData, TypeParent, TContext>;
@ -2834,6 +2853,19 @@ export namespace SourceResolvers {
Parent,
TContext
>;
export type AnomaliesOverTimeResolver<
R = AnomaliesOverTimeData,
Parent = Source,
TContext = SiemContext
> = Resolver<R, Parent, TContext, AnomaliesOverTimeArgs>;
export interface AnomaliesOverTimeArgs {
timerange: TimerangeInput;
filterQuery?: Maybe<string>;
defaultIndex: string[];
}
export type AuthenticationsResolver<
R = AuthenticationsData,
Parent = Source,
@ -3375,6 +3407,81 @@ export namespace IndexFieldResolvers {
> = Resolver<R, Parent, TContext>;
}
export namespace AnomaliesOverTimeDataResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = AnomaliesOverTimeData> {
inspect?: InspectResolver<Maybe<Inspect>, TypeParent, TContext>;
anomaliesOverTime?: AnomaliesOverTimeResolver<
MatrixOverTimeHistogramData[],
TypeParent,
TContext
>;
totalCount?: TotalCountResolver<number, TypeParent, TContext>;
}
export type InspectResolver<
R = Maybe<Inspect>,
Parent = AnomaliesOverTimeData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type AnomaliesOverTimeResolver<
R = MatrixOverTimeHistogramData[],
Parent = AnomaliesOverTimeData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type TotalCountResolver<
R = number,
Parent = AnomaliesOverTimeData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
}
export namespace InspectResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = Inspect> {
dsl?: DslResolver<string[], TypeParent, TContext>;
response?: ResponseResolver<string[], TypeParent, TContext>;
}
export type DslResolver<R = string[], Parent = Inspect, TContext = SiemContext> = Resolver<
R,
Parent,
TContext
>;
export type ResponseResolver<R = string[], Parent = Inspect, TContext = SiemContext> = Resolver<
R,
Parent,
TContext
>;
}
export namespace MatrixOverTimeHistogramDataResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = MatrixOverTimeHistogramData> {
x?: XResolver<number, TypeParent, TContext>;
y?: YResolver<number, TypeParent, TContext>;
g?: GResolver<string, TypeParent, TContext>;
}
export type XResolver<
R = number,
Parent = MatrixOverTimeHistogramData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type YResolver<
R = number,
Parent = MatrixOverTimeHistogramData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type GResolver<
R = string,
Parent = MatrixOverTimeHistogramData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
}
export namespace AuthenticationsDataResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = AuthenticationsData> {
edges?: EdgesResolver<AuthenticationsEdges[], TypeParent, TContext>;
@ -3820,25 +3927,6 @@ export namespace PageInfoPaginatedResolvers {
> = Resolver<R, Parent, TContext>;
}
export namespace InspectResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = Inspect> {
dsl?: DslResolver<string[], TypeParent, TContext>;
response?: ResponseResolver<string[], TypeParent, TContext>;
}
export type DslResolver<R = string[], Parent = Inspect, TContext = SiemContext> = Resolver<
R,
Parent,
TContext
>;
export type ResponseResolver<R = string[], Parent = Inspect, TContext = SiemContext> = Resolver<
R,
Parent,
TContext
>;
}
export namespace AuthenticationsOverTimeDataResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = AuthenticationsOverTimeData> {
inspect?: InspectResolver<Maybe<Inspect>, TypeParent, TContext>;
@ -3869,32 +3957,6 @@ export namespace AuthenticationsOverTimeDataResolvers {
> = Resolver<R, Parent, TContext>;
}
export namespace MatrixOverTimeHistogramDataResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = MatrixOverTimeHistogramData> {
x?: XResolver<number, TypeParent, TContext>;
y?: YResolver<number, TypeParent, TContext>;
g?: GResolver<string, TypeParent, TContext>;
}
export type XResolver<
R = number,
Parent = MatrixOverTimeHistogramData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type YResolver<
R = number,
Parent = MatrixOverTimeHistogramData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
export type GResolver<
R = string,
Parent = MatrixOverTimeHistogramData,
TContext = SiemContext
> = Resolver<R, Parent, TContext>;
}
export namespace TimelineDataResolvers {
export interface Resolvers<TContext = SiemContext, TypeParent = TimelineData> {
edges?: EdgesResolver<TimelineEdges[], TypeParent, TContext>;
@ -8645,6 +8707,9 @@ export type IResolvers<TContext = SiemContext> = {
SourceFields?: SourceFieldsResolvers.Resolvers<TContext>;
SourceStatus?: SourceStatusResolvers.Resolvers<TContext>;
IndexField?: IndexFieldResolvers.Resolvers<TContext>;
AnomaliesOverTimeData?: AnomaliesOverTimeDataResolvers.Resolvers<TContext>;
Inspect?: InspectResolvers.Resolvers<TContext>;
MatrixOverTimeHistogramData?: MatrixOverTimeHistogramDataResolvers.Resolvers<TContext>;
AuthenticationsData?: AuthenticationsDataResolvers.Resolvers<TContext>;
AuthenticationsEdges?: AuthenticationsEdgesResolvers.Resolvers<TContext>;
AuthenticationItem?: AuthenticationItemResolvers.Resolvers<TContext>;
@ -8657,9 +8722,7 @@ export type IResolvers<TContext = SiemContext> = {
OsEcsFields?: OsEcsFieldsResolvers.Resolvers<TContext>;
CursorType?: CursorTypeResolvers.Resolvers<TContext>;
PageInfoPaginated?: PageInfoPaginatedResolvers.Resolvers<TContext>;
Inspect?: InspectResolvers.Resolvers<TContext>;
AuthenticationsOverTimeData?: AuthenticationsOverTimeDataResolvers.Resolvers<TContext>;
MatrixOverTimeHistogramData?: MatrixOverTimeHistogramDataResolvers.Resolvers<TContext>;
TimelineData?: TimelineDataResolvers.Resolvers<TContext>;
TimelineEdges?: TimelineEdgesResolvers.Resolvers<TContext>;
TimelineItem?: TimelineItemResolvers.Resolvers<TContext>;

View file

@ -6,6 +6,7 @@
import { IResolvers, makeExecutableSchema } from 'graphql-tools';
import { schemas } from './graphql';
import { createAnomaliesResolvers } from './graphql/anomalies';
import { createAuthenticationsResolvers } from './graphql/authentications';
import { createScalarToStringArrayValueResolvers } from './graphql/ecs';
import { createEsValueResolvers, createEventsResolvers } from './graphql/events';
@ -32,6 +33,7 @@ import { createTlsResolvers } from './graphql/tls';
export const initServer = (libs: AppBackendLibs) => {
const schema = makeExecutableSchema({
resolvers: [
createAnomaliesResolvers(libs) as IResolvers,
createAuthenticationsResolvers(libs) as IResolvers,
createEsValueResolvers() as IResolvers,
createEventsResolvers(libs) as IResolvers,

View file

@ -0,0 +1,64 @@
/*
* 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 { AnomaliesOverTimeData } from '../../graphql/types';
import { inspectStringifyObject } from '../../utils/build_query';
import { FrameworkAdapter, FrameworkRequest, RequestBasicOptions } from '../framework';
import { TermAggregation } from '../types';
import { AnomalyHit, AnomaliesAdapter, AnomaliesActionGroupData } from './types';
import { buildAnomaliesOverTimeQuery } from './query.anomalies_over_time.dsl';
import { MatrixOverTimeHistogramData } from '../../../public/graphql/types';
export class ElasticsearchAnomaliesAdapter implements AnomaliesAdapter {
constructor(private readonly framework: FrameworkAdapter) {}
public async getAnomaliesOverTime(
request: FrameworkRequest,
options: RequestBasicOptions
): Promise<AnomaliesOverTimeData> {
const dsl = buildAnomaliesOverTimeQuery(options);
const response = await this.framework.callWithRequest<AnomalyHit, TermAggregation>(
request,
'search',
dsl
);
const totalCount = getOr(0, 'hits.total.value', response);
const anomaliesOverTimeBucket = getOr([], 'aggregations.anomalyActionGroup.buckets', response);
const inspect = {
dsl: [inspectStringifyObject(dsl)],
response: [inspectStringifyObject(response)],
};
return {
inspect,
anomaliesOverTime: getAnomaliesOverTimeByJobId(anomaliesOverTimeBucket),
totalCount,
};
}
}
const getAnomaliesOverTimeByJobId = (
data: AnomaliesActionGroupData[]
): MatrixOverTimeHistogramData[] => {
let result: MatrixOverTimeHistogramData[] = [];
data.forEach(({ key: group, anomalies }) => {
const anomaliesData = getOr([], 'buckets', anomalies).map(
({ key, doc_count }: { key: number; doc_count: number }) => ({
x: key,
y: doc_count,
g: group,
})
);
result = [...result, ...anomaliesData];
});
return result;
};

View file

@ -0,0 +1,21 @@
/*
* 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 { FrameworkRequest, RequestBasicOptions } from '../framework';
export * from './elasticsearch_adapter';
import { AnomaliesAdapter } from './types';
import { AnomaliesOverTimeData } from '../../../public/graphql/types';
export class Anomalies {
constructor(private readonly adapter: AnomaliesAdapter) {}
public async getAnomaliesOverTime(
req: FrameworkRequest,
options: RequestBasicOptions
): Promise<AnomaliesOverTimeData> {
return this.adapter.getAnomaliesOverTime(req, options);
}
}

View file

@ -0,0 +1,75 @@
/*
* 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 { createQueryFilterClauses, calculateTimeseriesInterval } from '../../utils/build_query';
import { RequestBasicOptions } from '../framework';
export const buildAnomaliesOverTimeQuery = ({
filterQuery,
timerange: { from, to },
defaultIndex,
}: RequestBasicOptions) => {
const filter = [
...createQueryFilterClauses(filterQuery),
{
range: {
timestamp: {
gte: from,
lte: to,
},
},
},
];
const getHistogramAggregation = () => {
const interval = calculateTimeseriesInterval(from, to);
const histogramTimestampField = 'timestamp';
const dateHistogram = {
date_histogram: {
field: histogramTimestampField,
fixed_interval: `${interval}s`,
},
};
const autoDateHistogram = {
auto_date_histogram: {
field: histogramTimestampField,
buckets: 36,
},
};
return {
anomalyActionGroup: {
terms: {
field: 'job_id',
order: {
_count: 'desc',
},
size: 10,
},
aggs: {
anomalies: interval ? dateHistogram : autoDateHistogram,
},
},
};
};
const dslQuery = {
index: defaultIndex,
allowNoIndices: true,
ignoreUnavailable: true,
body: {
aggs: getHistogramAggregation(),
query: {
bool: {
filter,
},
},
size: 0,
track_total_hits: true,
},
};
return dslQuery;
};

View file

@ -0,0 +1,42 @@
/*
* 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 { AnomaliesOverTimeData } from '../../graphql/types';
import { FrameworkRequest, RequestBasicOptions } from '../framework';
import { SearchHit } from '../types';
export interface AnomaliesAdapter {
getAnomaliesOverTime(
req: FrameworkRequest,
options: RequestBasicOptions
): Promise<AnomaliesOverTimeData>;
}
export interface AnomalySource {
[field: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
export interface AnomalyHit extends SearchHit {
sort: string[];
_source: AnomalySource;
aggregations: {
[agg: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
};
}
interface AnomaliesOverTimeHistogramData {
key_as_string: string;
key: number;
doc_count: number;
}
export interface AnomaliesActionGroupData {
key: number;
anomalies: {
bucket: AnomaliesOverTimeHistogramData[];
};
doc_count: number;
}

View file

@ -27,8 +27,7 @@ export const buildAuthenticationsOverTimeQuery = ({
];
const getHistogramAggregation = () => {
const minIntervalSeconds = 10;
const interval = calculateTimeseriesInterval(from, to, minIntervalSeconds);
const interval = calculateTimeseriesInterval(from, to);
const histogramTimestampField = '@timestamp';
const dateHistogram = {
date_histogram: {

View file

@ -6,6 +6,8 @@
import { EnvironmentMode } from 'src/core/server';
import { ServerFacade } from '../../types';
import { Anomalies } from '../anomalies';
import { ElasticsearchAnomaliesAdapter } from '../anomalies/elasticsearch_adapter';
import { Authentications } from '../authentications';
import { ElasticsearchAuthenticationAdapter } from '../authentications/elasticsearch_adapter';
import { KibanaConfigurationAdapter } from '../configuration/kibana_configuration_adapter';
@ -43,6 +45,7 @@ export function compose(server: ServerFacade, mode: EnvironmentMode): AppBackend
const pinnedEvent = new PinnedEvent({ savedObjects: framework.getSavedObjectsService() });
const domainLibs: AppDomainLibs = {
anomalies: new Anomalies(new ElasticsearchAnomaliesAdapter(framework)),
authentications: new Authentications(new ElasticsearchAuthenticationAdapter(framework)),
events: new Events(new ElasticsearchEventsAdapter(framework)),
fields: new IndexFields(new ElasticsearchIndexFieldAdapter(framework)),

View file

@ -27,8 +27,7 @@ export const buildEventsOverTimeQuery = ({
];
const getHistogramAggregation = () => {
const minIntervalSeconds = 10;
const interval = calculateTimeseriesInterval(from, to, minIntervalSeconds);
const interval = calculateTimeseriesInterval(from, to);
const histogramTimestampField = '@timestamp';
const dateHistogram = {
date_histogram: {

View file

@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Anomalies } from './anomalies';
import { Authentications } from './authentications';
import { ConfigurationAdapter } from './configuration';
import { Events } from './events';
@ -27,6 +28,7 @@ import { SignalAlertParamsRest } from './detection_engine/alerts/types';
export * from './hosts';
export interface AppDomainLibs {
anomalies: Anomalies;
authentications: Authentications;
events: Events;
fields: IndexFields;

View file

@ -91,8 +91,7 @@ export const calculateAuto = {
export const calculateTimeseriesInterval = (
lowerBoundInMsSinceEpoch: number,
upperBoundInMsSinceEpoch: number,
minIntervalSeconds: number
upperBoundInMsSinceEpoch: number
) => {
const duration = moment.duration(upperBoundInMsSinceEpoch - lowerBoundInMsSinceEpoch, 'ms');

View file

@ -9676,15 +9676,10 @@ deep-object-diff@^1.1.0:
resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.0.tgz#d6fabf476c2ed1751fc94d5ca693d2ed8c18bc5a"
integrity sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw==
deepmerge@3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.2.0.tgz#58ef463a57c08d376547f8869fdc5bcee957f44e"
integrity sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow==
deepmerge@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.0.0.tgz#3e3110ca29205f120d7cb064960a39c3d2087c09"
integrity sha512-YZ1rOP5+kHor4hMAH+HRQnBQHg+wvS1un1hAOuIcxcBy0hzcUf6Jg2a1w65kpoOUnurOfZbERwjI1TfZxNjcww==
deepmerge@3.2.0, deepmerge@^4.0.0, deepmerge@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
default-compare@^1.0.0:
version "1.0.0"