Skip to content

Commit

Permalink
[SIEM] Add hosts and network anomalies histogram (#50295)
Browse files Browse the repository at this point in the history
  • Loading branch information
patrykkopycinski authored Nov 25, 2019
1 parent b5133b5 commit f32c1e4
Show file tree
Hide file tree
Showing 42 changed files with 1,234 additions and 351 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,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": [
Expand Down Expand Up @@ -155,6 +156,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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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}
/>
);
};
Original file line number Diff line number Diff line change
@@ -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}}`,
});
Original file line number Diff line number Diff line change
@@ -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
}
}
}
}
`;
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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';
Original file line number Diff line number Diff line change
@@ -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;
};
Loading

0 comments on commit f32c1e4

Please sign in to comment.