mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
parent
41c2ab38a3
commit
5869552272
46 changed files with 8869 additions and 11 deletions
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 { EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { OverviewHostQuery } from '../../../../containers/overview/overview_host';
|
||||
import { OverviewHostStats } from '../overview_host_stats';
|
||||
|
||||
import * as i18n from '../translations';
|
||||
|
||||
export interface OwnProps {
|
||||
poll: number;
|
||||
startDate: number;
|
||||
endDate: number;
|
||||
}
|
||||
|
||||
export const OverviewHost = pure<OwnProps>(({ endDate, poll, startDate }) => (
|
||||
<EuiFlexItem>
|
||||
<EuiPanel>
|
||||
<EuiTitle>
|
||||
<h2>{i18n.HOSTS_HEADING}</h2>
|
||||
</EuiTitle>
|
||||
<OverviewHostQuery endDate={endDate} poll={poll} sourceId="default" startDate={startDate}>
|
||||
{({ overviewHost }) => <OverviewHostStats data={overviewHost} />}
|
||||
</OverviewHostQuery>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
));
|
|
@ -0,0 +1,16 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Overview Host Data rendering it renders the default OverviewHostStats 1`] = `
|
||||
<Component
|
||||
data={
|
||||
Object {
|
||||
"auditbeatAuditd": 73847,
|
||||
"auditbeatFIM": 107307,
|
||||
"auditbeatLogin": 60015,
|
||||
"auditbeatPackage": 2003,
|
||||
"auditbeatProcess": 1200,
|
||||
"auditbeatUser": 1979,
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
|
@ -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 { shallow } from 'enzyme';
|
||||
import toJson from 'enzyme-to-json';
|
||||
import * as React from 'react';
|
||||
|
||||
import { OverviewHostStats } from '.';
|
||||
import { mockData } from './mock';
|
||||
|
||||
describe('Overview Host Data', () => {
|
||||
describe('rendering', () => {
|
||||
test('it renders the default OverviewHostStats', () => {
|
||||
const wrapper = shallow(<OverviewHostStats data={mockData.OverviewHost} />);
|
||||
expect(toJson(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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 {
|
||||
// @ts-ignore
|
||||
EuiStat,
|
||||
} from '@elastic/eui';
|
||||
import numeral from '@elastic/numeral';
|
||||
import { has } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { OverviewHostData } from '../../../../graphql/types';
|
||||
import { getEmptyTagValue } from '../../../empty_value';
|
||||
|
||||
import * as i18n from '../translations';
|
||||
|
||||
interface OverviewHostProps {
|
||||
data: OverviewHostData;
|
||||
}
|
||||
|
||||
const overviewHostStats = (data: OverviewHostData) => [
|
||||
{
|
||||
title:
|
||||
has('auditbeatAuditd', data) && data.auditbeatAuditd !== null
|
||||
? numeral(data.auditbeatAuditd).format('0,0')
|
||||
: getEmptyTagValue(),
|
||||
description: i18n.AUDITBEAT_AUDITD,
|
||||
},
|
||||
{
|
||||
title:
|
||||
has('auditbeatFIM', data) && data.auditbeatFIM !== null
|
||||
? numeral(data.auditbeatFIM).format('0,0')
|
||||
: getEmptyTagValue(),
|
||||
description: i18n.AUDITBEAT_FIM,
|
||||
},
|
||||
{
|
||||
title:
|
||||
has('auditbeatLogin', data) && data.auditbeatLogin !== null
|
||||
? numeral(data.auditbeatLogin).format('0,0')
|
||||
: getEmptyTagValue(),
|
||||
description: i18n.AUDITBEAT_LOGIN,
|
||||
},
|
||||
{
|
||||
title:
|
||||
has('auditbeatPackage', data) && data.auditbeatPackage !== null
|
||||
? numeral(data.auditbeatPackage).format('0,0')
|
||||
: getEmptyTagValue(),
|
||||
description: i18n.AUDITBEAT_PACKAGE,
|
||||
},
|
||||
{
|
||||
title:
|
||||
has('auditbeatProcess', data) && data.auditbeatProcess !== null
|
||||
? numeral(data.auditbeatProcess).format('0,0')
|
||||
: getEmptyTagValue(),
|
||||
description: i18n.AUDITBEAT_PROCESS,
|
||||
},
|
||||
{
|
||||
title:
|
||||
has('auditbeatUser', data) && data.auditbeatUser !== null
|
||||
? numeral(data.auditbeatUser).format('0,0')
|
||||
: getEmptyTagValue(),
|
||||
description: i18n.AUDITBEAT_USER,
|
||||
},
|
||||
];
|
||||
export const OverviewHostStats = pure<OverviewHostProps>(({ data }) => (
|
||||
<>
|
||||
{overviewHostStats(data).map(item => (
|
||||
<EuiStat
|
||||
key={item.description}
|
||||
textAlign="center"
|
||||
title={item.title}
|
||||
description={item.description}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
));
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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 { OverviewHostData } from '../../../../graphql/types';
|
||||
|
||||
export const mockData: { OverviewHost: OverviewHostData } = {
|
||||
OverviewHost: {
|
||||
auditbeatAuditd: 73847,
|
||||
auditbeatFIM: 107307,
|
||||
auditbeatLogin: 60015,
|
||||
auditbeatPackage: 2003,
|
||||
auditbeatProcess: 1200,
|
||||
auditbeatUser: 1979,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 { EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { OverviewNetworkQuery } from '../../../../containers/overview/overview_network';
|
||||
import { OverviewNetworkStats } from '../overview_network_stats';
|
||||
|
||||
import * as i18n from '../translations';
|
||||
|
||||
export interface OwnProps {
|
||||
poll: number;
|
||||
startDate: number;
|
||||
endDate: number;
|
||||
}
|
||||
|
||||
export const OverviewNetwork = pure<OwnProps>(({ endDate, poll, startDate }) => (
|
||||
<EuiFlexItem>
|
||||
<EuiPanel>
|
||||
<EuiTitle>
|
||||
<h2>{i18n.NETWORK_HEADING}</h2>
|
||||
</EuiTitle>
|
||||
<OverviewNetworkQuery endDate={endDate} poll={poll} sourceId="default" startDate={startDate}>
|
||||
{({ overviewNetwork }) => <OverviewNetworkStats data={overviewNetwork} />}
|
||||
</OverviewNetworkQuery>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
));
|
|
@ -0,0 +1,15 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Overview Network Data rendering it renders the default OverviewNetworkStats 1`] = `
|
||||
<Component
|
||||
data={
|
||||
Object {
|
||||
"auditbeatSocket": 12,
|
||||
"filebeatSuricata": 60015,
|
||||
"filebeatZeek": 2003,
|
||||
"packetbeatDNS": 10277307,
|
||||
"packetbeatFlow": 16,
|
||||
}
|
||||
}
|
||||
/>
|
||||
`;
|
|
@ -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 { shallow } from 'enzyme';
|
||||
import toJson from 'enzyme-to-json';
|
||||
import * as React from 'react';
|
||||
|
||||
import { OverviewNetworkStats } from '.';
|
||||
import { mockData } from './mock';
|
||||
|
||||
describe('Overview Network Data', () => {
|
||||
describe('rendering', () => {
|
||||
test('it renders the default OverviewNetworkStats', () => {
|
||||
const wrapper = shallow(<OverviewNetworkStats data={mockData.OverviewNetwork} />);
|
||||
expect(toJson(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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 {
|
||||
// @ts-ignore
|
||||
EuiStat,
|
||||
} from '@elastic/eui';
|
||||
import numeral from '@elastic/numeral';
|
||||
import { has } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { OverviewNetworkData } from '../../../../graphql/types';
|
||||
import { getEmptyTagValue } from '../../../empty_value';
|
||||
|
||||
import * as i18n from '../translations';
|
||||
|
||||
interface OverviewNetworkProps {
|
||||
data: OverviewNetworkData;
|
||||
}
|
||||
|
||||
const overviewNetworkStats = (data: OverviewNetworkData) => [
|
||||
{
|
||||
title:
|
||||
has('packetbeatFlow', data) && data.packetbeatFlow !== null
|
||||
? numeral(data.packetbeatFlow).format('0,0')
|
||||
: getEmptyTagValue(),
|
||||
description: i18n.PACKETBEAT_FLOW,
|
||||
},
|
||||
{
|
||||
title:
|
||||
has('packetbeatDNS', data) && data.packetbeatDNS !== null
|
||||
? numeral(data.packetbeatDNS).format('0,0')
|
||||
: getEmptyTagValue(),
|
||||
description: i18n.PACKETBEAT_DNS,
|
||||
},
|
||||
{
|
||||
title:
|
||||
has('filebeatSuricata', data) && data.filebeatSuricata !== null
|
||||
? numeral(data.filebeatSuricata).format('0,0')
|
||||
: getEmptyTagValue(),
|
||||
description: i18n.FILEBEAT_SURICATA,
|
||||
},
|
||||
{
|
||||
title:
|
||||
has('filebeatZeek', data) && data.filebeatZeek !== null
|
||||
? numeral(data.filebeatZeek).format('0,0')
|
||||
: getEmptyTagValue(),
|
||||
description: i18n.FILEBEAT_ZEEK,
|
||||
},
|
||||
{
|
||||
title:
|
||||
has('auditbeatSocket', data) && data.auditbeatSocket !== null
|
||||
? numeral(data.auditbeatSocket).format('0,0')
|
||||
: getEmptyTagValue(),
|
||||
description: i18n.AUDITBEAT_SOCKET,
|
||||
},
|
||||
];
|
||||
export const OverviewNetworkStats = pure<OverviewNetworkProps>(({ data }) => (
|
||||
<>
|
||||
{overviewNetworkStats(data).map(item => (
|
||||
<EuiStat
|
||||
key={item.description}
|
||||
textAlign="center"
|
||||
title={item.title}
|
||||
description={item.description}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
));
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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 { OverviewNetworkData } from '../../../../graphql/types';
|
||||
|
||||
export const mockData: { OverviewNetwork: OverviewNetworkData } = {
|
||||
OverviewNetwork: {
|
||||
packetbeatFlow: 16,
|
||||
packetbeatDNS: 10277307,
|
||||
filebeatSuricata: 60015,
|
||||
filebeatZeek: 2003,
|
||||
auditbeatSocket: 12,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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 PACKETBEAT_FLOW = i18n.translate(
|
||||
'xpack.secops.overviewNetwork.source.packetbeatFlowTitle',
|
||||
{
|
||||
defaultMessage: 'Packetbeat Flow',
|
||||
}
|
||||
);
|
||||
|
||||
export const PACKETBEAT_DNS = i18n.translate(
|
||||
'xpack.secops.overviewNetwork.source.packetbeatDNSTitle',
|
||||
{
|
||||
defaultMessage: 'Packetbeat DNS',
|
||||
}
|
||||
);
|
||||
|
||||
export const FILEBEAT_SURICATA = i18n.translate(
|
||||
'xpack.secops.overviewNetwork.source.filebeatSuricataTitle',
|
||||
{
|
||||
defaultMessage: 'Filebeat Suricata',
|
||||
}
|
||||
);
|
||||
|
||||
export const FILEBEAT_ZEEK = i18n.translate(
|
||||
'xpack.secops.overviewNetwork.source.filebeatZeekTitle',
|
||||
{
|
||||
defaultMessage: 'Filebeat Zeek',
|
||||
}
|
||||
);
|
||||
|
||||
export const AUDITBEAT_SOCKET = i18n.translate(
|
||||
'xpack.secops.overviewNetwork.source.auditbeatSocketTitle',
|
||||
{
|
||||
defaultMessage: 'Auditbeat Socket',
|
||||
}
|
||||
);
|
||||
|
||||
export const HOSTS_HEADING = i18n.translate('xpack.secops.overview.hosts.headingTitle', {
|
||||
defaultMessage: 'Hosts',
|
||||
});
|
||||
|
||||
export const NETWORK_HEADING = i18n.translate('xpack.secops.overview.network.headingTitle', {
|
||||
defaultMessage: 'Network',
|
||||
});
|
||||
|
||||
export const AUDITBEAT_AUDITD = i18n.translate(
|
||||
'xpack.secops.overviewNetwork.source.auditbeatAuditdTitle',
|
||||
{
|
||||
defaultMessage: 'Auditbeat Auditd',
|
||||
}
|
||||
);
|
||||
|
||||
export const AUDITBEAT_FIM = i18n.translate(
|
||||
'xpack.secops.overviewNetwork.source.auditbeatFIMTitle',
|
||||
{
|
||||
defaultMessage: 'Auditbeat FIM',
|
||||
}
|
||||
);
|
||||
|
||||
export const AUDITBEAT_LOGIN = i18n.translate(
|
||||
'xpack.secops.overviewNetwork.source.auditbeatLoginTitle',
|
||||
{
|
||||
defaultMessage: 'Auditbeat Login',
|
||||
}
|
||||
);
|
||||
|
||||
export const AUDITBEAT_PACKAGE = i18n.translate(
|
||||
'xpack.secops.overviewNetwork.source.auditbeatPackageTitle',
|
||||
{
|
||||
defaultMessage: 'Auditbeat Package',
|
||||
}
|
||||
);
|
||||
|
||||
export const AUDITBEAT_PROCESS = i18n.translate(
|
||||
'xpack.secops.overviewNetwork.source.auditbeatProcessTitle',
|
||||
{
|
||||
defaultMessage: 'Auditbeat Process',
|
||||
}
|
||||
);
|
||||
|
||||
export const AUDITBEAT_USER = i18n.translate(
|
||||
'xpack.secops.overviewNetwork.source.auditbeatUserTitle',
|
||||
{
|
||||
defaultMessage: 'Auditbeat User',
|
||||
}
|
||||
);
|
|
@ -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 overviewHostQuery = gql`
|
||||
query GetOverviewHostQuery($sourceId: ID!, $timerange: TimerangeInput!, $filterQuery: String) {
|
||||
source(id: $sourceId) {
|
||||
id
|
||||
OverviewHost(timerange: $timerange, filterQuery: $filterQuery) {
|
||||
auditbeatAuditd
|
||||
auditbeatFIM
|
||||
auditbeatLogin
|
||||
auditbeatPackage
|
||||
auditbeatProcess
|
||||
auditbeatUser
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 { pure } from 'recompose';
|
||||
|
||||
import { GetOverviewHostQuery, OverviewHostData } from '../../../graphql/types';
|
||||
import { createFilter } from '../../helpers';
|
||||
import { QueryTemplateProps } from '../../query_template';
|
||||
|
||||
import { overviewHostQuery } from './index.gql_query';
|
||||
|
||||
export interface OverviewHostArgs {
|
||||
id: string;
|
||||
overviewHost: OverviewHostData;
|
||||
}
|
||||
|
||||
export interface OverviewHostProps extends QueryTemplateProps {
|
||||
children: (args: OverviewHostArgs) => React.ReactNode;
|
||||
sourceId: string;
|
||||
endDate: number;
|
||||
poll: number;
|
||||
startDate: number;
|
||||
}
|
||||
|
||||
export const OverviewHostQuery = pure<OverviewHostProps>(
|
||||
({ id = 'overviewHostQuery', children, filterQuery, sourceId, startDate, endDate }) => (
|
||||
<Query<GetOverviewHostQuery.Query, GetOverviewHostQuery.Variables>
|
||||
query={overviewHostQuery}
|
||||
fetchPolicy="cache-and-network"
|
||||
variables={{
|
||||
sourceId,
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
from: startDate,
|
||||
to: endDate,
|
||||
},
|
||||
filterQuery: createFilter(filterQuery),
|
||||
}}
|
||||
>
|
||||
{({ data }) => {
|
||||
const overviewHost = getOr({}, `source.OverviewHost`, data);
|
||||
return children({
|
||||
id,
|
||||
overviewHost,
|
||||
});
|
||||
}}
|
||||
</Query>
|
||||
)
|
||||
);
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 overviewNetworkQuery = gql`
|
||||
query GetOverviewNetworkQuery($sourceId: ID!, $timerange: TimerangeInput!, $filterQuery: String) {
|
||||
source(id: $sourceId) {
|
||||
id
|
||||
OverviewNetwork(timerange: $timerange, filterQuery: $filterQuery) {
|
||||
packetbeatFlow
|
||||
packetbeatDNS
|
||||
filebeatSuricata
|
||||
filebeatZeek
|
||||
auditbeatSocket
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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 { pure } from 'recompose';
|
||||
|
||||
import { GetOverviewNetworkQuery, OverviewNetworkData } from '../../../graphql/types';
|
||||
import { createFilter } from '../../helpers';
|
||||
import { QueryTemplateProps } from '../../query_template';
|
||||
|
||||
import { overviewNetworkQuery } from './index.gql_query';
|
||||
|
||||
export interface OverviewNetworkArgs {
|
||||
id: string;
|
||||
overviewNetwork: OverviewNetworkData;
|
||||
}
|
||||
|
||||
export interface OverviewNetworkProps extends QueryTemplateProps {
|
||||
children: (args: OverviewNetworkArgs) => React.ReactNode;
|
||||
sourceId: string;
|
||||
endDate: number;
|
||||
poll: number;
|
||||
startDate: number;
|
||||
}
|
||||
|
||||
export const OverviewNetworkQuery = pure<OverviewNetworkProps>(
|
||||
({ id = 'overviewNetworkQuery', children, filterQuery, sourceId, startDate, endDate }) => (
|
||||
<Query<GetOverviewNetworkQuery.Query, GetOverviewNetworkQuery.Variables>
|
||||
query={overviewNetworkQuery}
|
||||
fetchPolicy="cache-and-network"
|
||||
notifyOnNetworkStatusChange
|
||||
variables={{
|
||||
sourceId,
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
from: startDate,
|
||||
to: endDate,
|
||||
},
|
||||
filterQuery: createFilter(filterQuery),
|
||||
}}
|
||||
>
|
||||
{({ data }) => {
|
||||
const overviewNetwork = getOr({}, `source.OverviewNetwork`, data);
|
||||
return children({
|
||||
id,
|
||||
overviewNetwork,
|
||||
});
|
||||
}}
|
||||
</Query>
|
||||
)
|
||||
);
|
|
@ -544,6 +544,68 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "OverviewNetwork",
|
||||
"description": "",
|
||||
"args": [
|
||||
{
|
||||
"name": "id",
|
||||
"description": "",
|
||||
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"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
|
||||
}
|
||||
],
|
||||
"type": { "kind": "OBJECT", "name": "OverviewNetworkData", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "OverviewHost",
|
||||
"description": "",
|
||||
"args": [
|
||||
{
|
||||
"name": "id",
|
||||
"description": "",
|
||||
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"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
|
||||
}
|
||||
],
|
||||
"type": { "kind": "OBJECT", "name": "OverviewHostData", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "UncommonProcesses",
|
||||
"description": "Gets UncommonProcesses based on a timerange, or all UncommonProcesses if no criteria is specified",
|
||||
|
@ -4987,6 +5049,140 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "OverviewNetworkData",
|
||||
"description": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "packetbeatFlow",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "packetbeatDNS",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "filebeatSuricata",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "filebeatZeek",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Float", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "auditbeatSocket",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Float", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "OverviewHostData",
|
||||
"description": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "auditbeatAuditd",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "auditbeatFIM",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "auditbeatLogin",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "SCALAR", "name": "Float", "ofType": null }
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "auditbeatPackage",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Float", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "auditbeatProcess",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Float", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "auditbeatUser",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Float", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "UncommonProcessesData",
|
||||
|
|
|
@ -55,6 +55,10 @@ export interface Source {
|
|||
NetworkTopNFlow: NetworkTopNFlowData;
|
||||
|
||||
NetworkDns: NetworkDnsData;
|
||||
|
||||
OverviewNetwork?: OverviewNetworkData | null;
|
||||
|
||||
OverviewHost?: OverviewHostData | null;
|
||||
/** Gets UncommonProcesses based on a timerange, or all UncommonProcesses if no criteria is specified */
|
||||
UncommonProcesses: UncommonProcessesData;
|
||||
/** Just a simple example to get the app name */
|
||||
|
@ -873,6 +877,32 @@ export interface NetworkDnsItem {
|
|||
uniqueDomains?: number | null;
|
||||
}
|
||||
|
||||
export interface OverviewNetworkData {
|
||||
packetbeatFlow: number;
|
||||
|
||||
packetbeatDNS: number;
|
||||
|
||||
filebeatSuricata: number;
|
||||
|
||||
filebeatZeek?: number | null;
|
||||
|
||||
auditbeatSocket?: number | null;
|
||||
}
|
||||
|
||||
export interface OverviewHostData {
|
||||
auditbeatAuditd: number;
|
||||
|
||||
auditbeatFIM: number;
|
||||
|
||||
auditbeatLogin: number;
|
||||
|
||||
auditbeatPackage?: number | null;
|
||||
|
||||
auditbeatProcess?: number | null;
|
||||
|
||||
auditbeatUser?: number | null;
|
||||
}
|
||||
|
||||
export interface UncommonProcessesData {
|
||||
edges: UncommonProcessesEdges[];
|
||||
|
||||
|
@ -1035,6 +1065,20 @@ export interface NetworkDnsSourceArgs {
|
|||
|
||||
timerange: TimerangeInput;
|
||||
}
|
||||
export interface OverviewNetworkSourceArgs {
|
||||
id?: string | null;
|
||||
|
||||
timerange: TimerangeInput;
|
||||
|
||||
filterQuery?: string | null;
|
||||
}
|
||||
export interface OverviewHostSourceArgs {
|
||||
id?: string | null;
|
||||
|
||||
timerange: TimerangeInput;
|
||||
|
||||
filterQuery?: string | null;
|
||||
}
|
||||
export interface UncommonProcessesSourceArgs {
|
||||
timerange: TimerangeInput;
|
||||
|
||||
|
@ -2070,6 +2114,80 @@ export namespace GetNetworkTopNFlowQuery {
|
|||
};
|
||||
}
|
||||
|
||||
export namespace GetOverviewHostQuery {
|
||||
export type Variables = {
|
||||
sourceId: string;
|
||||
timerange: TimerangeInput;
|
||||
filterQuery?: string | null;
|
||||
};
|
||||
|
||||
export type Query = {
|
||||
__typename?: 'Query';
|
||||
|
||||
source: Source;
|
||||
};
|
||||
|
||||
export type Source = {
|
||||
__typename?: 'Source';
|
||||
|
||||
id: string;
|
||||
|
||||
OverviewHost?: OverviewHost | null;
|
||||
};
|
||||
|
||||
export type OverviewHost = {
|
||||
__typename?: 'OverviewHostData';
|
||||
|
||||
auditbeatAuditd: number;
|
||||
|
||||
auditbeatFIM: number;
|
||||
|
||||
auditbeatLogin: number;
|
||||
|
||||
auditbeatPackage?: number | null;
|
||||
|
||||
auditbeatProcess?: number | null;
|
||||
|
||||
auditbeatUser?: number | null;
|
||||
};
|
||||
}
|
||||
|
||||
export namespace GetOverviewNetworkQuery {
|
||||
export type Variables = {
|
||||
sourceId: string;
|
||||
timerange: TimerangeInput;
|
||||
filterQuery?: string | null;
|
||||
};
|
||||
|
||||
export type Query = {
|
||||
__typename?: 'Query';
|
||||
|
||||
source: Source;
|
||||
};
|
||||
|
||||
export type Source = {
|
||||
__typename?: 'Source';
|
||||
|
||||
id: string;
|
||||
|
||||
OverviewNetwork?: OverviewNetwork | null;
|
||||
};
|
||||
|
||||
export type OverviewNetwork = {
|
||||
__typename?: 'OverviewNetworkData';
|
||||
|
||||
packetbeatFlow: number;
|
||||
|
||||
packetbeatDNS: number;
|
||||
|
||||
filebeatSuricata: number;
|
||||
|
||||
filebeatZeek?: number | null;
|
||||
|
||||
auditbeatSocket?: number | null;
|
||||
};
|
||||
}
|
||||
|
||||
export namespace SourceQuery {
|
||||
export type Variables = {
|
||||
sourceId?: string | null;
|
||||
|
|
|
@ -7,15 +7,6 @@
|
|||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { Pane, Pane1FlexContent, PaneScrollContainer } from '../../components/page';
|
||||
import { Placeholders } from '../../components/visualization_placeholder';
|
||||
import { OverviewComponent } from './overview';
|
||||
|
||||
export const Overview = pure(() => (
|
||||
<Pane data-test-subj="pane">
|
||||
<PaneScrollContainer data-test-subj="pane1ScrollContainer">
|
||||
<Pane1FlexContent>
|
||||
<Placeholders count={10} myRoute="Overview" />
|
||||
</Pane1FlexContent>
|
||||
</PaneScrollContainer>
|
||||
</Pane>
|
||||
));
|
||||
export const Overview = pure(() => <OverviewComponent />);
|
||||
|
|
34
x-pack/plugins/secops/public/pages/overview/overview.tsx
Normal file
34
x-pack/plugins/secops/public/pages/overview/overview.tsx
Normal 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 { EuiFlexGroup } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { OverviewHost } from '../../components/page/overview/overview_host';
|
||||
import { OverviewNetwork } from '../../components/page/overview/overview_network';
|
||||
import { GlobalTime } from '../../containers/global_time';
|
||||
import { PageContent, PageContentBody } from '../styles';
|
||||
|
||||
import { Summary } from './summary';
|
||||
import { Welcome } from './welcome';
|
||||
|
||||
export const OverviewComponent = pure(() => (
|
||||
<PageContent>
|
||||
<Welcome />
|
||||
<PageContentBody>
|
||||
<Summary />
|
||||
<GlobalTime>
|
||||
{({ poll, to, from }) => (
|
||||
<EuiFlexGroup>
|
||||
<OverviewHost poll={poll} endDate={to} startDate={from} />
|
||||
<OverviewNetwork poll={poll} endDate={to} startDate={from} />
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
</GlobalTime>
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
));
|
103
x-pack/plugins/secops/public/pages/overview/summary.tsx
Normal file
103
x-pack/plugins/secops/public/pages/overview/summary.tsx
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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 { EuiFlexGroup, EuiFlexItem, EuiLink, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import * as i18n from './translations';
|
||||
|
||||
interface ListItemType {
|
||||
label: JSX.Element;
|
||||
}
|
||||
|
||||
const listContent: ListItemType[] = [
|
||||
{
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="xpack.secops.overview.feature.documentation"
|
||||
defaultMessage="Check out the {link}"
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink href="#">
|
||||
<FormattedMessage
|
||||
id="xpack.secops.overview.feature.documentation.link"
|
||||
defaultMessage="Documentation"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="xpack.secops.overview.feature.dataIngestion"
|
||||
defaultMessage="Learn about {link}"
|
||||
values={{
|
||||
link: (
|
||||
<EuiLink href="#">
|
||||
<FormattedMessage
|
||||
id="xpack.secops.overview.feature.dataIngestion.link"
|
||||
defaultMessage="Data Ingestion"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="xpack.secops.overview.feature.blog"
|
||||
defaultMessage="Go to {blog} and {video}"
|
||||
values={{
|
||||
blog: (
|
||||
<EuiLink href="#">
|
||||
<FormattedMessage
|
||||
id="xpack.secops.overview.feature.blog.blog"
|
||||
defaultMessage="Blog Posts"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
video: (
|
||||
<EuiLink href="#">
|
||||
<FormattedMessage
|
||||
id="xpack.secops.overview.feature.blog.video"
|
||||
defaultMessage="Videos"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export const Summary = pure(() => (
|
||||
<EuiFlexGroup justifyContent="spaceAround">
|
||||
<StyledEuiFlexItem grow={false}>
|
||||
<EuiTitle size="s">
|
||||
<h3>{i18n.FEATURE_OVERVIEW_HEADING}</h3>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
<ol>
|
||||
{listContent.map(({ label }, i) => (
|
||||
<li key={`${i}-${label}`}>{label}</li>
|
||||
))}
|
||||
</ol>
|
||||
</EuiText>
|
||||
</StyledEuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
));
|
||||
|
||||
const StyledEuiFlexItem = styled(EuiFlexItem)`
|
||||
min-width: 350px;
|
||||
`;
|
19
x-pack/plugins/secops/public/pages/overview/translations.ts
Normal file
19
x-pack/plugins/secops/public/pages/overview/translations.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const OVERVIEW_TITLE = i18n.translate('xpack.secops.overview.sectionTitle', {
|
||||
defaultMessage: 'Welcome to the SIEM App!',
|
||||
});
|
||||
|
||||
export const OVERVIEW_SUBTITLE = i18n.translate('xpack.secops.overview.subTitle', {
|
||||
defaultMessage: 'Security Information and Event Management with the Elastic Stack',
|
||||
});
|
||||
|
||||
export const FEATURE_OVERVIEW_HEADING = i18n.translate('xpack.secops.overview.featureTitle', {
|
||||
defaultMessage: 'What you can do here:',
|
||||
});
|
26
x-pack/plugins/secops/public/pages/overview/welcome.tsx
Normal file
26
x-pack/plugins/secops/public/pages/overview/welcome.tsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiText, EuiTextAlign, EuiTitle } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import * as i18n from './translations';
|
||||
|
||||
export const Welcome = pure(() => (
|
||||
<EuiFlexGroup justifyContent="spaceAround">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTextAlign textAlign="center">
|
||||
<EuiTitle size="l">
|
||||
<h1>{i18n.OVERVIEW_TITLE}</h1>
|
||||
</EuiTitle>
|
||||
<EuiText>
|
||||
<p>{i18n.OVERVIEW_SUBTITLE}</p>
|
||||
</EuiText>
|
||||
</EuiTextAlign>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
));
|
|
@ -17,6 +17,7 @@ import { hostsSchema } from './hosts';
|
|||
import { ipOverviewSchema } from './ip_overview';
|
||||
import { kpiNetworkSchema } from './kpi_network';
|
||||
import { networkSchema } from './network';
|
||||
import { overviewSchema } from './overview';
|
||||
import { dateSchema } from './scalar_date';
|
||||
import { sourceStatusSchema } from './source_status';
|
||||
import { sourcesSchema } from './sources';
|
||||
|
@ -32,6 +33,7 @@ export const schemas = [
|
|||
ipOverviewSchema,
|
||||
kpiNetworkSchema,
|
||||
networkSchema,
|
||||
overviewSchema,
|
||||
rootSchema,
|
||||
sourcesSchema,
|
||||
sourceStatusSchema,
|
||||
|
|
8
x-pack/plugins/secops/server/graphql/overview/index.ts
Normal file
8
x-pack/plugins/secops/server/graphql/overview/index.ts
Normal 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 { createOverviewResolvers } from './resolvers';
|
||||
export { overviewSchema } from './schema.gql';
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 { Logger } from '../../utils/logger';
|
||||
import { SecOpsContext } from '../index';
|
||||
import { OverviewHostData, OverviewNetworkData } from '../types';
|
||||
|
||||
export const mockOverviewNetworkData: { OverviewNetwork: OverviewNetworkData } = {
|
||||
OverviewNetwork: {
|
||||
packetbeatFlow: 0,
|
||||
packetbeatDNS: 0,
|
||||
filebeatSuricata: 0,
|
||||
filebeatZeek: 0,
|
||||
auditbeatSocket: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export const getOverviewNetworkQueryMock = (logger: Logger) => ({
|
||||
source: (root: unknown, args: unknown, context: SecOpsContext) => {
|
||||
logger.info('Mock source');
|
||||
const operationName = context.req.payload.operationName.toLowerCase();
|
||||
switch (operationName) {
|
||||
case 'test': {
|
||||
logger.info(`Using mock for test ${mockOverviewNetworkData}`);
|
||||
return mockOverviewNetworkData;
|
||||
}
|
||||
default: {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const mockOverviewHostData: { OverviewHost: OverviewHostData } = {
|
||||
OverviewHost: {
|
||||
auditbeatAuditd: 0,
|
||||
auditbeatFIM: 0,
|
||||
auditbeatLogin: 0,
|
||||
auditbeatPackage: 0,
|
||||
auditbeatProcess: 0,
|
||||
auditbeatUser: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export const getOverviewHostQueryMock = (logger: Logger) => ({
|
||||
source: (root: unknown, args: unknown, context: SecOpsContext) => {
|
||||
logger.info('Mock source');
|
||||
const operationName = context.req.payload.operationName.toLowerCase();
|
||||
switch (operationName) {
|
||||
case 'test': {
|
||||
logger.info(`Using mock for test ${mockOverviewHostData}`);
|
||||
return mockOverviewHostData;
|
||||
}
|
||||
default: {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
111
x-pack/plugins/secops/server/graphql/overview/resolvers.test.ts
Normal file
111
x-pack/plugins/secops/server/graphql/overview/resolvers.test.ts
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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 { GraphQLResolveInfo } from 'graphql';
|
||||
|
||||
import { Source } from '../../graphql/types';
|
||||
import { FrameworkRequest, internalFrameworkRequest } from '../../lib/framework';
|
||||
import { Overview } from '../../lib/overview';
|
||||
import { OverviewAdapter } from '../../lib/overview/types';
|
||||
import { SourceStatus } from '../../lib/source_status';
|
||||
import { Sources } from '../../lib/sources';
|
||||
import { createSourcesResolvers } from '../sources';
|
||||
import { SourcesResolversDeps } from '../sources/resolvers';
|
||||
import { mockSourcesAdapter, mockSourceStatusAdapter } from '../sources/resolvers.test';
|
||||
|
||||
import { mockOverviewHostData, mockOverviewNetworkData } from './overview.mock';
|
||||
import { createOverviewResolvers, OverviewResolversDeps } from './resolvers';
|
||||
|
||||
const mockGetOverviewNetwork = jest.fn();
|
||||
mockGetOverviewNetwork.mockResolvedValue({
|
||||
OverviewNetwork: {
|
||||
...mockOverviewNetworkData.OverviewNetwork,
|
||||
},
|
||||
});
|
||||
|
||||
const mockGetOverviewHost = jest.fn();
|
||||
mockGetOverviewHost.mockResolvedValue({
|
||||
OverviewHost: {
|
||||
...mockOverviewHostData.OverviewHost,
|
||||
},
|
||||
});
|
||||
|
||||
const mockOverviewAdapter: OverviewAdapter = {
|
||||
getOverviewHost: mockGetOverviewHost,
|
||||
getOverviewNetwork: mockGetOverviewNetwork,
|
||||
};
|
||||
|
||||
const mockOverviewLibs: OverviewResolversDeps = {
|
||||
overview: new Overview(mockOverviewAdapter),
|
||||
};
|
||||
|
||||
const mockSrcLibs: SourcesResolversDeps = {
|
||||
sources: new Sources(mockSourcesAdapter),
|
||||
sourceStatus: new SourceStatus(mockSourceStatusAdapter, new Sources(mockSourcesAdapter)),
|
||||
};
|
||||
|
||||
const req: FrameworkRequest = {
|
||||
[internalFrameworkRequest]: {
|
||||
params: {},
|
||||
query: {},
|
||||
payload: {
|
||||
operationName: 'test',
|
||||
},
|
||||
},
|
||||
params: {},
|
||||
query: {},
|
||||
payload: {
|
||||
operationName: 'test',
|
||||
},
|
||||
};
|
||||
|
||||
const context = { req };
|
||||
|
||||
describe('Test Overview SIEM Resolvers', () => {
|
||||
test('Make sure that getOverviewNetwork have been called', async () => {
|
||||
const source = await createSourcesResolvers(mockSrcLibs).Query.source(
|
||||
{},
|
||||
{ id: 'default' },
|
||||
context,
|
||||
{} as GraphQLResolveInfo
|
||||
);
|
||||
const data = await createOverviewResolvers(mockOverviewLibs).Source.OverviewNetwork(
|
||||
source as Source,
|
||||
{
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
to: 1514782800000,
|
||||
from: 1546318799999,
|
||||
},
|
||||
},
|
||||
context,
|
||||
{} as GraphQLResolveInfo
|
||||
);
|
||||
expect(mockOverviewAdapter.getOverviewNetwork).toHaveBeenCalled();
|
||||
expect(data).toEqual(mockOverviewNetworkData);
|
||||
});
|
||||
test('Make sure that getOverviewHost have been called', async () => {
|
||||
const source = await createSourcesResolvers(mockSrcLibs).Query.source(
|
||||
{},
|
||||
{ id: 'default' },
|
||||
context,
|
||||
{} as GraphQLResolveInfo
|
||||
);
|
||||
const data = await createOverviewResolvers(mockOverviewLibs).Source.OverviewHost(
|
||||
source as Source,
|
||||
{
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
to: 1514782800000,
|
||||
from: 1546318799999,
|
||||
},
|
||||
},
|
||||
context,
|
||||
{} as GraphQLResolveInfo
|
||||
);
|
||||
expect(mockOverviewAdapter.getOverviewHost).toHaveBeenCalled();
|
||||
expect(data).toEqual(mockOverviewHostData);
|
||||
});
|
||||
});
|
45
x-pack/plugins/secops/server/graphql/overview/resolvers.ts
Normal file
45
x-pack/plugins/secops/server/graphql/overview/resolvers.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 { SourceResolvers } from '../../graphql/types';
|
||||
import { AppResolverOf, ChildResolverOf } from '../../lib/framework';
|
||||
import { Overview } from '../../lib/overview';
|
||||
import { createOptions } from '../../utils/build_query/create_options';
|
||||
import { QuerySourceResolver } from '../sources/resolvers';
|
||||
|
||||
export type QueryOverviewNetworkResolver = ChildResolverOf<
|
||||
AppResolverOf<SourceResolvers.OverviewNetworkResolver>,
|
||||
QuerySourceResolver
|
||||
>;
|
||||
|
||||
export type QueryOverviewHostResolver = ChildResolverOf<
|
||||
AppResolverOf<SourceResolvers.OverviewHostResolver>,
|
||||
QuerySourceResolver
|
||||
>;
|
||||
|
||||
export interface OverviewResolversDeps {
|
||||
overview: Overview;
|
||||
}
|
||||
|
||||
export const createOverviewResolvers = (
|
||||
libs: OverviewResolversDeps
|
||||
): {
|
||||
Source: {
|
||||
OverviewHost: QueryOverviewHostResolver;
|
||||
OverviewNetwork: QueryOverviewNetworkResolver;
|
||||
};
|
||||
} => ({
|
||||
Source: {
|
||||
async OverviewNetwork(source, args, { req }, info) {
|
||||
const options = { ...createOptions(source, args, info) };
|
||||
return libs.overview.getOverviewNetwork(req, options);
|
||||
},
|
||||
async OverviewHost(source, args, { req }, info) {
|
||||
const options = { ...createOptions(source, args, info) };
|
||||
return libs.overview.getOverviewHost(req, options);
|
||||
},
|
||||
},
|
||||
});
|
35
x-pack/plugins/secops/server/graphql/overview/schema.gql.ts
Normal file
35
x-pack/plugins/secops/server/graphql/overview/schema.gql.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 overviewSchema = gql`
|
||||
type OverviewNetworkData {
|
||||
packetbeatFlow: Float!
|
||||
packetbeatDNS: Float!
|
||||
filebeatSuricata: Float!
|
||||
filebeatZeek: Float
|
||||
auditbeatSocket: Float
|
||||
}
|
||||
|
||||
type OverviewHostData {
|
||||
auditbeatAuditd: Float!
|
||||
auditbeatFIM: Float!
|
||||
auditbeatLogin: Float!
|
||||
auditbeatPackage: Float
|
||||
auditbeatProcess: Float
|
||||
auditbeatUser: Float
|
||||
}
|
||||
|
||||
extend type Source {
|
||||
OverviewNetwork(
|
||||
id: String
|
||||
timerange: TimerangeInput!
|
||||
filterQuery: String
|
||||
): OverviewNetworkData
|
||||
OverviewHost(id: String, timerange: TimerangeInput!, filterQuery: String): OverviewHostData
|
||||
}
|
||||
`;
|
191
x-pack/plugins/secops/server/graphql/overview/schema.test.ts
Normal file
191
x-pack/plugins/secops/server/graphql/overview/schema.test.ts
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* 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 { graphql } from 'graphql';
|
||||
import { addMockFunctionsToSchema, makeExecutableSchema } from 'graphql-tools';
|
||||
|
||||
import { rootSchema } from '../../../common/graphql/root/schema.gql';
|
||||
import { sharedSchema } from '../../../common/graphql/shared';
|
||||
import { Logger } from '../../utils/logger';
|
||||
import { ecsSchema } from '../ecs';
|
||||
import { dateSchema } from '../scalar_date';
|
||||
import { sourceStatusSchema } from '../source_status/schema.gql';
|
||||
import { sourcesSchema } from '../sources/schema.gql';
|
||||
|
||||
import {
|
||||
getOverviewHostQueryMock,
|
||||
getOverviewNetworkQueryMock,
|
||||
mockOverviewHostData,
|
||||
mockOverviewNetworkData,
|
||||
} from './overview.mock';
|
||||
import { overviewSchema } from './schema.gql';
|
||||
|
||||
const testOverviewNetworkSource = {
|
||||
id: 'Test case to query Siem Overview Network data',
|
||||
query: `
|
||||
query GetOverviewNetworkQuery(
|
||||
$timerange: TimerangeInput!
|
||||
$filterQuery: String
|
||||
) {
|
||||
source(id: "default") {
|
||||
OverviewNetwork(timerange: $timerange, filterQuery: $filterQuery) {
|
||||
packetbeatFlow
|
||||
packetbeatDNS
|
||||
filebeatSuricata
|
||||
filebeatZeek
|
||||
auditbeatSocket
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
to: 1514782800000,
|
||||
from: 1546318799999,
|
||||
},
|
||||
},
|
||||
context: {
|
||||
req: {
|
||||
payload: {
|
||||
operationName: 'test',
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: {
|
||||
data: {
|
||||
source: {
|
||||
...mockOverviewNetworkData,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const testOverviewHostSource = {
|
||||
id: 'Test case to query Siem Overview Host data',
|
||||
query: `
|
||||
query GetOverviewHostQuery(
|
||||
$timerange: TimerangeInput!
|
||||
$filterQuery: String
|
||||
) {
|
||||
source(id: "default") {
|
||||
OverviewHost(timerange: $timerange, filterQuery: $filterQuery) {
|
||||
auditbeatAuditd
|
||||
auditbeatFIM
|
||||
auditbeatLogin
|
||||
auditbeatPackage
|
||||
auditbeatProcess
|
||||
auditbeatUser
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
to: 1514782800000,
|
||||
from: 1546318799999,
|
||||
},
|
||||
},
|
||||
context: {
|
||||
req: {
|
||||
payload: {
|
||||
operationName: 'test',
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: {
|
||||
data: {
|
||||
source: {
|
||||
...mockOverviewHostData,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
describe('SIEM Overview GQL Schema', () => {
|
||||
describe('Test Host Schema', () => {
|
||||
// Array of case types
|
||||
const cases = [testOverviewHostSource];
|
||||
const typeDefs = [
|
||||
rootSchema,
|
||||
sharedSchema,
|
||||
sourcesSchema,
|
||||
sourceStatusSchema,
|
||||
ecsSchema,
|
||||
overviewSchema,
|
||||
dateSchema,
|
||||
];
|
||||
const mockSchema = makeExecutableSchema({ typeDefs });
|
||||
|
||||
// Here we specify the return payloads of mocked types
|
||||
const logger: Logger = {
|
||||
debug: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
};
|
||||
const mocks = {
|
||||
Query: () => ({
|
||||
...getOverviewHostQueryMock(logger),
|
||||
}),
|
||||
};
|
||||
|
||||
addMockFunctionsToSchema({
|
||||
schema: mockSchema,
|
||||
mocks,
|
||||
});
|
||||
|
||||
cases.forEach(obj => {
|
||||
const { id, query, variables, context, expected } = obj;
|
||||
|
||||
test(`${id}`, async () => {
|
||||
const result = await graphql(mockSchema, query, null, context, variables);
|
||||
return await expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Test Network Schema', () => {
|
||||
// Array of case types
|
||||
const cases = [testOverviewNetworkSource];
|
||||
const typeDefs = [
|
||||
rootSchema,
|
||||
sharedSchema,
|
||||
sourcesSchema,
|
||||
sourceStatusSchema,
|
||||
ecsSchema,
|
||||
overviewSchema,
|
||||
dateSchema,
|
||||
];
|
||||
const mockSchema = makeExecutableSchema({ typeDefs });
|
||||
|
||||
// Here we specify the return payloads of mocked types
|
||||
const logger: Logger = {
|
||||
debug: jest.fn(),
|
||||
info: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
};
|
||||
const mocks = {
|
||||
Query: () => ({
|
||||
...getOverviewNetworkQueryMock(logger),
|
||||
}),
|
||||
};
|
||||
|
||||
addMockFunctionsToSchema({
|
||||
schema: mockSchema,
|
||||
mocks,
|
||||
});
|
||||
|
||||
cases.forEach(obj => {
|
||||
const { id, query, variables, context, expected } = obj;
|
||||
|
||||
test(`${id}`, async () => {
|
||||
const result = await graphql(mockSchema, query, null, context, variables);
|
||||
return await expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -84,6 +84,10 @@ export interface Source {
|
|||
NetworkTopNFlow: NetworkTopNFlowData;
|
||||
|
||||
NetworkDns: NetworkDnsData;
|
||||
|
||||
OverviewNetwork?: OverviewNetworkData | null;
|
||||
|
||||
OverviewHost?: OverviewHostData | null;
|
||||
/** Gets UncommonProcesses based on a timerange, or all UncommonProcesses if no criteria is specified */
|
||||
UncommonProcesses: UncommonProcessesData;
|
||||
/** Just a simple example to get the app name */
|
||||
|
@ -902,6 +906,32 @@ export interface NetworkDnsItem {
|
|||
uniqueDomains?: number | null;
|
||||
}
|
||||
|
||||
export interface OverviewNetworkData {
|
||||
packetbeatFlow: number;
|
||||
|
||||
packetbeatDNS: number;
|
||||
|
||||
filebeatSuricata: number;
|
||||
|
||||
filebeatZeek?: number | null;
|
||||
|
||||
auditbeatSocket?: number | null;
|
||||
}
|
||||
|
||||
export interface OverviewHostData {
|
||||
auditbeatAuditd: number;
|
||||
|
||||
auditbeatFIM: number;
|
||||
|
||||
auditbeatLogin: number;
|
||||
|
||||
auditbeatPackage?: number | null;
|
||||
|
||||
auditbeatProcess?: number | null;
|
||||
|
||||
auditbeatUser?: number | null;
|
||||
}
|
||||
|
||||
export interface UncommonProcessesData {
|
||||
edges: UncommonProcessesEdges[];
|
||||
|
||||
|
@ -1064,6 +1094,20 @@ export interface NetworkDnsSourceArgs {
|
|||
|
||||
timerange: TimerangeInput;
|
||||
}
|
||||
export interface OverviewNetworkSourceArgs {
|
||||
id?: string | null;
|
||||
|
||||
timerange: TimerangeInput;
|
||||
|
||||
filterQuery?: string | null;
|
||||
}
|
||||
export interface OverviewHostSourceArgs {
|
||||
id?: string | null;
|
||||
|
||||
timerange: TimerangeInput;
|
||||
|
||||
filterQuery?: string | null;
|
||||
}
|
||||
export interface UncommonProcessesSourceArgs {
|
||||
timerange: TimerangeInput;
|
||||
|
||||
|
@ -1193,6 +1237,10 @@ export namespace SourceResolvers {
|
|||
NetworkTopNFlow?: NetworkTopNFlowResolver<NetworkTopNFlowData, TypeParent, Context>;
|
||||
|
||||
NetworkDns?: NetworkDnsResolver<NetworkDnsData, TypeParent, Context>;
|
||||
|
||||
OverviewNetwork?: OverviewNetworkResolver<OverviewNetworkData | null, TypeParent, Context>;
|
||||
|
||||
OverviewHost?: OverviewHostResolver<OverviewHostData | null, TypeParent, Context>;
|
||||
/** Gets UncommonProcesses based on a timerange, or all UncommonProcesses if no criteria is specified */
|
||||
UncommonProcesses?: UncommonProcessesResolver<UncommonProcessesData, TypeParent, Context>;
|
||||
/** Just a simple example to get the app name */
|
||||
|
@ -1353,6 +1401,32 @@ export namespace SourceResolvers {
|
|||
timerange: TimerangeInput;
|
||||
}
|
||||
|
||||
export type OverviewNetworkResolver<
|
||||
R = OverviewNetworkData | null,
|
||||
Parent = Source,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context, OverviewNetworkArgs>;
|
||||
export interface OverviewNetworkArgs {
|
||||
id?: string | null;
|
||||
|
||||
timerange: TimerangeInput;
|
||||
|
||||
filterQuery?: string | null;
|
||||
}
|
||||
|
||||
export type OverviewHostResolver<
|
||||
R = OverviewHostData | null,
|
||||
Parent = Source,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context, OverviewHostArgs>;
|
||||
export interface OverviewHostArgs {
|
||||
id?: string | null;
|
||||
|
||||
timerange: TimerangeInput;
|
||||
|
||||
filterQuery?: string | null;
|
||||
}
|
||||
|
||||
export type UncommonProcessesResolver<
|
||||
R = UncommonProcessesData,
|
||||
Parent = Source,
|
||||
|
@ -4076,6 +4150,93 @@ export namespace NetworkDnsItemResolvers {
|
|||
> = Resolver<R, Parent, Context>;
|
||||
}
|
||||
|
||||
export namespace OverviewNetworkDataResolvers {
|
||||
export interface Resolvers<Context = SecOpsContext, TypeParent = OverviewNetworkData> {
|
||||
packetbeatFlow?: PacketbeatFlowResolver<number, TypeParent, Context>;
|
||||
|
||||
packetbeatDNS?: PacketbeatDnsResolver<number, TypeParent, Context>;
|
||||
|
||||
filebeatSuricata?: FilebeatSuricataResolver<number, TypeParent, Context>;
|
||||
|
||||
filebeatZeek?: FilebeatZeekResolver<number | null, TypeParent, Context>;
|
||||
|
||||
auditbeatSocket?: AuditbeatSocketResolver<number | null, TypeParent, Context>;
|
||||
}
|
||||
|
||||
export type PacketbeatFlowResolver<
|
||||
R = number,
|
||||
Parent = OverviewNetworkData,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type PacketbeatDnsResolver<
|
||||
R = number,
|
||||
Parent = OverviewNetworkData,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type FilebeatSuricataResolver<
|
||||
R = number,
|
||||
Parent = OverviewNetworkData,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type FilebeatZeekResolver<
|
||||
R = number | null,
|
||||
Parent = OverviewNetworkData,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type AuditbeatSocketResolver<
|
||||
R = number | null,
|
||||
Parent = OverviewNetworkData,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
}
|
||||
|
||||
export namespace OverviewHostDataResolvers {
|
||||
export interface Resolvers<Context = SecOpsContext, TypeParent = OverviewHostData> {
|
||||
auditbeatAuditd?: AuditbeatAuditdResolver<number, TypeParent, Context>;
|
||||
|
||||
auditbeatFIM?: AuditbeatFimResolver<number, TypeParent, Context>;
|
||||
|
||||
auditbeatLogin?: AuditbeatLoginResolver<number, TypeParent, Context>;
|
||||
|
||||
auditbeatPackage?: AuditbeatPackageResolver<number | null, TypeParent, Context>;
|
||||
|
||||
auditbeatProcess?: AuditbeatProcessResolver<number | null, TypeParent, Context>;
|
||||
|
||||
auditbeatUser?: AuditbeatUserResolver<number | null, TypeParent, Context>;
|
||||
}
|
||||
|
||||
export type AuditbeatAuditdResolver<
|
||||
R = number,
|
||||
Parent = OverviewHostData,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type AuditbeatFimResolver<
|
||||
R = number,
|
||||
Parent = OverviewHostData,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type AuditbeatLoginResolver<
|
||||
R = number,
|
||||
Parent = OverviewHostData,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type AuditbeatPackageResolver<
|
||||
R = number | null,
|
||||
Parent = OverviewHostData,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type AuditbeatProcessResolver<
|
||||
R = number | null,
|
||||
Parent = OverviewHostData,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type AuditbeatUserResolver<
|
||||
R = number | null,
|
||||
Parent = OverviewHostData,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
}
|
||||
|
||||
export namespace UncommonProcessesDataResolvers {
|
||||
export interface Resolvers<Context = SecOpsContext, TypeParent = UncommonProcessesData> {
|
||||
edges?: EdgesResolver<UncommonProcessesEdges[], TypeParent, Context>;
|
||||
|
|
|
@ -13,6 +13,7 @@ import { createHostsResolvers } from './graphql/hosts';
|
|||
import { createIpOverviewResolvers } from './graphql/ip_overview';
|
||||
import { createKpiNetworkResolvers } from './graphql/kpi_network';
|
||||
import { createNetworkResolvers } from './graphql/network';
|
||||
import { createOverviewResolvers } from './graphql/overview';
|
||||
import { createScalarDateResolvers } from './graphql/scalar_date';
|
||||
import { createSourceStatusResolvers } from './graphql/source_status';
|
||||
import { createSourcesResolvers } from './graphql/sources';
|
||||
|
@ -37,6 +38,7 @@ export const initServer = (libs: AppBackendLibs, config: Config) => {
|
|||
createIpOverviewResolvers(libs) as IResolvers,
|
||||
createSourcesResolvers(libs) as IResolvers,
|
||||
createScalarToStringArrayValueResolvers() as IResolvers,
|
||||
createOverviewResolvers(libs) as IResolvers,
|
||||
createNetworkResolvers(libs) as IResolvers,
|
||||
createScalarDateResolvers() as IResolvers,
|
||||
createSourcesResolvers(libs) as IResolvers,
|
||||
|
|
|
@ -17,6 +17,8 @@ import { ElasticsearchIpOverviewAdapter, IpOverview } from '../ip_overview';
|
|||
import { KpiNetwork } from '../kpi_network';
|
||||
import { ElasticsearchKpiNetworkAdapter } from '../kpi_network/elasticsearch_adapter';
|
||||
import { ElasticsearchNetworkAdapter, Network } from '../network';
|
||||
import { Overview } from '../overview';
|
||||
import { ElasticsearchOverviewAdapter } from '../overview/elasticsearch_adapter';
|
||||
import { ElasticsearchSourceStatusAdapter, SourceStatus } from '../source_status';
|
||||
import { ConfigurationSourcesAdapter, Sources } from '../sources';
|
||||
import { AppBackendLibs, AppDomainLibs, Configuration } from '../types';
|
||||
|
@ -36,6 +38,7 @@ export function compose(server: Server): AppBackendLibs {
|
|||
ipOverview: new IpOverview(new ElasticsearchIpOverviewAdapter(framework)),
|
||||
kpiNetwork: new KpiNetwork(new ElasticsearchKpiNetworkAdapter(framework)),
|
||||
network: new Network(new ElasticsearchNetworkAdapter(framework)),
|
||||
overview: new Overview(new ElasticsearchOverviewAdapter(framework)),
|
||||
uncommonProcesses: new UncommonProcesses(new ElasticsearchUncommonProcessesAdapter(framework)),
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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 { cloneDeep } from 'lodash/fp';
|
||||
|
||||
import { OverviewHostData, OverviewNetworkData } from '../../graphql/types';
|
||||
import { FrameworkAdapter, FrameworkRequest } from '../framework';
|
||||
|
||||
import { ElasticsearchOverviewAdapter } from './elasticsearch_adapter';
|
||||
import {
|
||||
mockOptionsHost,
|
||||
mockOptionsNetwork,
|
||||
mockRequestHost,
|
||||
mockRequestNetwork,
|
||||
mockResponseHost,
|
||||
mockResponseNetwork,
|
||||
mockResultHost,
|
||||
mockResultNetwork,
|
||||
} from './mock';
|
||||
|
||||
describe('Siem Overview elasticsearch_adapter', () => {
|
||||
describe('Network Stats', () => {
|
||||
describe('Happy Path - get Data', () => {
|
||||
const mockCallWithRequest = jest.fn();
|
||||
mockCallWithRequest.mockResolvedValue(mockResponseNetwork);
|
||||
const mockFramework: FrameworkAdapter = {
|
||||
version: 'mock',
|
||||
callWithRequest: mockCallWithRequest,
|
||||
exposeStaticDir: jest.fn(),
|
||||
registerGraphQLEndpoint: jest.fn(),
|
||||
getIndexPatternsService: jest.fn(),
|
||||
};
|
||||
jest.mock('../framework', () => ({
|
||||
callWithRequest: mockCallWithRequest,
|
||||
}));
|
||||
|
||||
test('getOverviewNetwork', async () => {
|
||||
const EsOverviewNetwork = new ElasticsearchOverviewAdapter(mockFramework);
|
||||
const data: OverviewNetworkData = await EsOverviewNetwork.getOverviewNetwork(
|
||||
mockRequestNetwork as FrameworkRequest,
|
||||
mockOptionsNetwork
|
||||
);
|
||||
expect(data).toEqual(mockResultNetwork);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Unhappy Path - No data', () => {
|
||||
const mockNoDataResponse = cloneDeep(mockResponseNetwork);
|
||||
mockNoDataResponse.aggregations.unique_flow_count.doc_count = 0;
|
||||
mockNoDataResponse.aggregations.unique_dns_count.doc_count = 0;
|
||||
mockNoDataResponse.aggregations.unique_suricata_count.doc_count = 0;
|
||||
mockNoDataResponse.aggregations.unique_zeek_count.doc_count = 0;
|
||||
mockNoDataResponse.aggregations.unique_socket_count.doc_count = 0;
|
||||
const mockCallWithRequest = jest.fn();
|
||||
mockCallWithRequest.mockResolvedValue(mockNoDataResponse);
|
||||
const mockFramework: FrameworkAdapter = {
|
||||
version: 'mock',
|
||||
callWithRequest: mockCallWithRequest,
|
||||
exposeStaticDir: jest.fn(),
|
||||
registerGraphQLEndpoint: jest.fn(),
|
||||
getIndexPatternsService: jest.fn(),
|
||||
};
|
||||
jest.mock('../framework', () => ({
|
||||
callWithRequest: mockCallWithRequest,
|
||||
}));
|
||||
|
||||
test('getOverviewNetwork', async () => {
|
||||
const EsOverviewNetwork = new ElasticsearchOverviewAdapter(mockFramework);
|
||||
const data: OverviewNetworkData = await EsOverviewNetwork.getOverviewNetwork(
|
||||
mockRequestNetwork as FrameworkRequest,
|
||||
mockOptionsNetwork
|
||||
);
|
||||
expect(data).toEqual({
|
||||
packetbeatFlow: 0,
|
||||
packetbeatDNS: 0,
|
||||
filebeatSuricata: 0,
|
||||
filebeatZeek: 0,
|
||||
auditbeatSocket: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Host Stats', () => {
|
||||
describe('Happy Path - get Data', () => {
|
||||
const mockCallWithRequest = jest.fn();
|
||||
mockCallWithRequest.mockResolvedValue(mockResponseHost);
|
||||
const mockFramework: FrameworkAdapter = {
|
||||
version: 'mock',
|
||||
callWithRequest: mockCallWithRequest,
|
||||
exposeStaticDir: jest.fn(),
|
||||
registerGraphQLEndpoint: jest.fn(),
|
||||
getIndexPatternsService: jest.fn(),
|
||||
};
|
||||
jest.mock('../framework', () => ({
|
||||
callWithRequest: mockCallWithRequest,
|
||||
}));
|
||||
|
||||
test('getOverviewHost', async () => {
|
||||
const EsOverviewHost = new ElasticsearchOverviewAdapter(mockFramework);
|
||||
const data: OverviewHostData = await EsOverviewHost.getOverviewHost(
|
||||
mockRequestHost as FrameworkRequest,
|
||||
mockOptionsHost
|
||||
);
|
||||
expect(data).toEqual(mockResultHost);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Unhappy Path - No data', () => {
|
||||
const mockNoDataResponse = cloneDeep(mockResponseHost);
|
||||
mockNoDataResponse.aggregations.auditd_count.doc_count = 0;
|
||||
mockNoDataResponse.aggregations.fim_count.doc_count = 0;
|
||||
mockNoDataResponse.aggregations.system_module.login_count.doc_count = 0;
|
||||
mockNoDataResponse.aggregations.system_module.package_count.doc_count = 0;
|
||||
mockNoDataResponse.aggregations.system_module.process_count.doc_count = 0;
|
||||
mockNoDataResponse.aggregations.system_module.user_count.doc_count = 0;
|
||||
const mockCallWithRequest = jest.fn();
|
||||
mockCallWithRequest.mockResolvedValue(mockNoDataResponse);
|
||||
const mockFramework: FrameworkAdapter = {
|
||||
version: 'mock',
|
||||
callWithRequest: mockCallWithRequest,
|
||||
exposeStaticDir: jest.fn(),
|
||||
registerGraphQLEndpoint: jest.fn(),
|
||||
getIndexPatternsService: jest.fn(),
|
||||
};
|
||||
jest.mock('../framework', () => ({
|
||||
callWithRequest: mockCallWithRequest,
|
||||
}));
|
||||
|
||||
test('getOverviewHost', async () => {
|
||||
const EsOverviewHost = new ElasticsearchOverviewAdapter(mockFramework);
|
||||
const data: OverviewHostData = await EsOverviewHost.getOverviewHost(
|
||||
mockRequestHost as FrameworkRequest,
|
||||
mockOptionsHost
|
||||
);
|
||||
expect(data).toEqual({
|
||||
auditbeatAuditd: 0,
|
||||
auditbeatFIM: 0,
|
||||
auditbeatLogin: 0,
|
||||
auditbeatPackage: 0,
|
||||
auditbeatProcess: 0,
|
||||
auditbeatUser: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 { OverviewHostData, OverviewNetworkData } from '../../graphql/types';
|
||||
import { FrameworkAdapter, FrameworkRequest, RequestBasicOptions } from '../framework';
|
||||
import { TermAggregation } from '../types';
|
||||
|
||||
import { buildOverviewHostQuery, buildOverviewNetworkQuery } from './query.dsl';
|
||||
import { OverviewAdapter, OverviewHostHit, OverviewNetworkHit } from './types';
|
||||
|
||||
export class ElasticsearchOverviewAdapter implements OverviewAdapter {
|
||||
constructor(private readonly framework: FrameworkAdapter) {}
|
||||
|
||||
public async getOverviewNetwork(
|
||||
request: FrameworkRequest,
|
||||
options: RequestBasicOptions
|
||||
): Promise<OverviewNetworkData> {
|
||||
const response = await this.framework.callWithRequest<OverviewNetworkHit, TermAggregation>(
|
||||
request,
|
||||
'search',
|
||||
buildOverviewNetworkQuery(options)
|
||||
);
|
||||
|
||||
return {
|
||||
packetbeatFlow: getOr(null, 'aggregations.unique_flow_count.doc_count', response),
|
||||
packetbeatDNS: getOr(null, 'aggregations.unique_dns_count.doc_count', response),
|
||||
filebeatSuricata: getOr(null, 'aggregations.unique_suricata_count.doc_count', response),
|
||||
filebeatZeek: getOr(null, 'aggregations.unique_zeek_count.doc_count', response),
|
||||
auditbeatSocket: getOr(null, 'aggregations.unique_socket_count.doc_count', response),
|
||||
};
|
||||
}
|
||||
|
||||
public async getOverviewHost(
|
||||
request: FrameworkRequest,
|
||||
options: RequestBasicOptions
|
||||
): Promise<OverviewHostData> {
|
||||
const response = await this.framework.callWithRequest<OverviewHostHit, TermAggregation>(
|
||||
request,
|
||||
'search',
|
||||
buildOverviewHostQuery(options)
|
||||
);
|
||||
|
||||
return {
|
||||
auditbeatAuditd: getOr(null, 'aggregations.auditd_count.doc_count', response),
|
||||
auditbeatFIM: getOr(null, 'aggregations.fim_count.doc_count', response),
|
||||
auditbeatLogin: getOr(null, 'aggregations.system_module.login_count.doc_count', response),
|
||||
auditbeatPackage: getOr(null, 'aggregations.system_module.package_count.doc_count', response),
|
||||
auditbeatProcess: getOr(null, 'aggregations.system_module.process_count.doc_count', response),
|
||||
auditbeatUser: getOr(null, 'aggregations.system_module.user_count.doc_count', response),
|
||||
};
|
||||
}
|
||||
}
|
28
x-pack/plugins/secops/server/lib/overview/index.ts
Normal file
28
x-pack/plugins/secops/server/lib/overview/index.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 { OverviewHostData, OverviewNetworkData } from '../../graphql/types';
|
||||
import { FrameworkRequest, RequestBasicOptions } from '../framework';
|
||||
|
||||
import { OverviewAdapter } from './types';
|
||||
|
||||
export class Overview {
|
||||
constructor(private readonly adapter: OverviewAdapter) {}
|
||||
|
||||
public async getOverviewNetwork(
|
||||
req: FrameworkRequest,
|
||||
options: RequestBasicOptions
|
||||
): Promise<OverviewNetworkData> {
|
||||
return await this.adapter.getOverviewNetwork(req, options);
|
||||
}
|
||||
|
||||
public async getOverviewHost(
|
||||
req: FrameworkRequest,
|
||||
options: RequestBasicOptions
|
||||
): Promise<OverviewHostData> {
|
||||
return await this.adapter.getOverviewHost(req, options);
|
||||
}
|
||||
}
|
122
x-pack/plugins/secops/server/lib/overview/mock.ts
Normal file
122
x-pack/plugins/secops/server/lib/overview/mock.ts
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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 { RequestBasicOptions } from '../framework/types';
|
||||
|
||||
export const mockOptionsNetwork: RequestBasicOptions = {
|
||||
sourceConfiguration: {
|
||||
logAlias: 'filebeat-*',
|
||||
auditbeatAlias: 'auditbeat-*',
|
||||
packetbeatAlias: 'packetbeat-*',
|
||||
fields: {
|
||||
container: 'docker.container.name',
|
||||
host: 'beat.hostname',
|
||||
message: ['message', '@message'],
|
||||
pod: 'kubernetes.pod.name',
|
||||
tiebreaker: '_doc',
|
||||
timestamp: '@timestamp',
|
||||
},
|
||||
},
|
||||
timerange: { interval: '12h', to: 1549852006071, from: 1549765606071 },
|
||||
filterQuery: {},
|
||||
};
|
||||
|
||||
export const mockRequestNetwork = {
|
||||
params: {},
|
||||
payload: {
|
||||
operationName: 'GetOverviewNetworkQuery',
|
||||
variables: {
|
||||
sourceId: 'default',
|
||||
timerange: { interval: '12h', from: 1549765830772, to: 1549852230772 },
|
||||
filterQuery: '',
|
||||
},
|
||||
query:
|
||||
'query GetOverviewNetworkQuery(\n $sourceId: ID!\n $timerange: TimerangeInput!\n $filterQuery: String\n ) {\n source(id: $sourceId) {\n id\n OverviewNetwork(timerange: $timerange, filterQuery: $filterQuery) {\n packetbeatFlow\n packetbeatDNS\n filebeatSuricata\n filebeatZeek\n auditbeatSocket\n }\n }\n }',
|
||||
},
|
||||
query: {},
|
||||
};
|
||||
|
||||
export const mockResponseNetwork = {
|
||||
took: 89,
|
||||
timed_out: false,
|
||||
_shards: { total: 18, successful: 18, skipped: 0, failed: 0 },
|
||||
hits: { total: { value: 950867, relation: 'eq' }, max_score: null, hits: [] },
|
||||
aggregations: {
|
||||
unique_flow_count: { doc_count: 50243 },
|
||||
unique_dns_count: { doc_count: 15000 },
|
||||
unique_suricata_count: { doc_count: 2375 },
|
||||
unique_zeek_count: { doc_count: 456 },
|
||||
unique_socket_count: { doc_count: 13 },
|
||||
},
|
||||
};
|
||||
|
||||
export const mockResultNetwork = {
|
||||
packetbeatFlow: 50243,
|
||||
packetbeatDNS: 15000,
|
||||
filebeatSuricata: 2375,
|
||||
filebeatZeek: 456,
|
||||
auditbeatSocket: 13,
|
||||
};
|
||||
|
||||
export const mockOptionsHost: RequestBasicOptions = {
|
||||
sourceConfiguration: {
|
||||
logAlias: 'filebeat-*',
|
||||
auditbeatAlias: 'auditbeat-*',
|
||||
packetbeatAlias: 'packetbeat-*',
|
||||
fields: {
|
||||
container: 'docker.container.name',
|
||||
host: 'beat.hostname',
|
||||
message: ['message', '@message'],
|
||||
pod: 'kubernetes.pod.name',
|
||||
tiebreaker: '_doc',
|
||||
timestamp: '@timestamp',
|
||||
},
|
||||
},
|
||||
timerange: { interval: '12h', to: 1549852006071, from: 1549765606071 },
|
||||
filterQuery: {},
|
||||
};
|
||||
|
||||
export const mockRequestHost = {
|
||||
params: {},
|
||||
payload: {
|
||||
operationName: 'GetOverviewHostQuery',
|
||||
variables: {
|
||||
sourceId: 'default',
|
||||
timerange: { interval: '12h', from: 1549765830772, to: 1549852230772 },
|
||||
filterQuery: '',
|
||||
},
|
||||
query:
|
||||
'query GetOverviewHostQuery(\n $sourceId: ID!\n $timerange: TimerangeInput!\n $filterQuery: String\n ) {\n source(id: $sourceId) {\n id\n OverviewHost(timerange: $timerange, filterQuery: $filterQuery) {\n auditbeatAuditd\n auditbeatFIM\n auditbeatLogin\n auditbeatPackage\n auditbeatProcess\n auditbeatUser\n }\n }\n }',
|
||||
},
|
||||
query: {},
|
||||
};
|
||||
|
||||
export const mockResponseHost = {
|
||||
took: 89,
|
||||
timed_out: false,
|
||||
_shards: { total: 18, successful: 18, skipped: 0, failed: 0 },
|
||||
hits: { total: { value: 950867, relation: 'eq' }, max_score: null, hits: [] },
|
||||
aggregations: {
|
||||
auditd_count: { doc_count: 73847 },
|
||||
fim_count: { doc_count: 107307 },
|
||||
system_module: {
|
||||
doc_count: 20000000,
|
||||
login_count: { doc_count: 60015 },
|
||||
package_count: { doc_count: 2003 },
|
||||
process_count: { doc_count: 1200 },
|
||||
user_count: { doc_count: 1979 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const mockResultHost = {
|
||||
auditbeatAuditd: 73847,
|
||||
auditbeatFIM: 107307,
|
||||
auditbeatLogin: 60015,
|
||||
auditbeatPackage: 2003,
|
||||
auditbeatProcess: 1200,
|
||||
auditbeatUser: 1979,
|
||||
};
|
167
x-pack/plugins/secops/server/lib/overview/query.dsl.ts
Normal file
167
x-pack/plugins/secops/server/lib/overview/query.dsl.ts
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* 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 } from '../../utils/build_query';
|
||||
import { RequestBasicOptions } from '../framework';
|
||||
|
||||
export const buildOverviewNetworkQuery = ({
|
||||
filterQuery,
|
||||
timerange: { from, to },
|
||||
sourceConfiguration: {
|
||||
fields: { timestamp },
|
||||
auditbeatAlias,
|
||||
logAlias,
|
||||
packetbeatAlias,
|
||||
},
|
||||
}: RequestBasicOptions) => {
|
||||
const filter = [
|
||||
...createQueryFilterClauses(filterQuery),
|
||||
{
|
||||
range: {
|
||||
[timestamp]: {
|
||||
gte: from,
|
||||
lte: to,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const dslQuery = {
|
||||
allowNoIndices: true,
|
||||
index: [auditbeatAlias, logAlias, packetbeatAlias],
|
||||
ignoreUnavailable: true,
|
||||
body: {
|
||||
aggregations: {
|
||||
unique_flow_count: {
|
||||
filter: {
|
||||
term: { type: 'flow' },
|
||||
},
|
||||
},
|
||||
unique_dns_count: {
|
||||
filter: {
|
||||
term: { type: 'dns' },
|
||||
},
|
||||
},
|
||||
unique_suricata_count: {
|
||||
filter: {
|
||||
term: { 'service.type': 'suricata' },
|
||||
},
|
||||
},
|
||||
unique_zeek_count: {
|
||||
filter: {
|
||||
term: { 'service.type': 'zeek' },
|
||||
},
|
||||
},
|
||||
unique_socket_count: {
|
||||
filter: {
|
||||
term: { 'event.dataset': 'socket' },
|
||||
},
|
||||
},
|
||||
},
|
||||
query: {
|
||||
bool: {
|
||||
filter,
|
||||
},
|
||||
},
|
||||
size: 0,
|
||||
track_total_hits: true,
|
||||
},
|
||||
};
|
||||
|
||||
return dslQuery;
|
||||
};
|
||||
|
||||
export const buildOverviewHostQuery = ({
|
||||
filterQuery,
|
||||
timerange: { from, to },
|
||||
sourceConfiguration: {
|
||||
fields: { timestamp },
|
||||
auditbeatAlias,
|
||||
logAlias,
|
||||
packetbeatAlias,
|
||||
},
|
||||
}: RequestBasicOptions) => {
|
||||
const filter = [
|
||||
...createQueryFilterClauses(filterQuery),
|
||||
{
|
||||
range: {
|
||||
[timestamp]: {
|
||||
gte: from,
|
||||
lte: to,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const dslQuery = {
|
||||
allowNoIndices: true,
|
||||
index: [auditbeatAlias],
|
||||
ignoreUnavailable: true,
|
||||
body: {
|
||||
aggregations: {
|
||||
auditd_count: {
|
||||
filter: {
|
||||
term: {
|
||||
'event.module': 'auditd',
|
||||
},
|
||||
},
|
||||
},
|
||||
fim_count: {
|
||||
filter: {
|
||||
term: {
|
||||
'event.module': 'file_integrity',
|
||||
},
|
||||
},
|
||||
},
|
||||
system_module: {
|
||||
filter: {
|
||||
term: {
|
||||
'event.module': 'system',
|
||||
},
|
||||
},
|
||||
aggs: {
|
||||
login_count: {
|
||||
filter: {
|
||||
term: {
|
||||
'event.dataset': 'login',
|
||||
},
|
||||
},
|
||||
},
|
||||
package_count: {
|
||||
filter: {
|
||||
term: {
|
||||
'event.dataset': 'package',
|
||||
},
|
||||
},
|
||||
},
|
||||
process_count: {
|
||||
filter: {
|
||||
term: {
|
||||
'event.dataset': 'process',
|
||||
},
|
||||
},
|
||||
},
|
||||
user_count: {
|
||||
filter: {
|
||||
term: {
|
||||
'event.dataset': 'user',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
query: {
|
||||
bool: {
|
||||
filter,
|
||||
},
|
||||
},
|
||||
size: 0,
|
||||
track_total_hits: true,
|
||||
},
|
||||
};
|
||||
|
||||
return dslQuery;
|
||||
};
|
64
x-pack/plugins/secops/server/lib/overview/types.ts
Normal file
64
x-pack/plugins/secops/server/lib/overview/types.ts
Normal 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 { OverviewHostData, OverviewNetworkData } from '../../graphql/types';
|
||||
import { FrameworkRequest, RequestBasicOptions } from '../framework';
|
||||
import { SearchHit } from '../types';
|
||||
|
||||
export interface OverviewAdapter {
|
||||
getOverviewNetwork(
|
||||
request: FrameworkRequest,
|
||||
options: RequestBasicOptions
|
||||
): Promise<OverviewNetworkData>;
|
||||
getOverviewHost(
|
||||
request: FrameworkRequest,
|
||||
options: RequestBasicOptions
|
||||
): Promise<OverviewHostData>;
|
||||
}
|
||||
|
||||
export interface OverviewNetworkHit extends SearchHit {
|
||||
aggregations: {
|
||||
unique_flow_count: {
|
||||
doc_count: number;
|
||||
};
|
||||
unique_dns_count: {
|
||||
doc_count: number;
|
||||
};
|
||||
unique_suricata_count: {
|
||||
doc_count: number;
|
||||
};
|
||||
unique_zeek_count: {
|
||||
doc_count: number;
|
||||
};
|
||||
unique_socket_count: {
|
||||
doc_count: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface OverviewHostHit extends SearchHit {
|
||||
aggregations: {
|
||||
auditd_count: {
|
||||
doc_count: number;
|
||||
};
|
||||
fim_count: {
|
||||
doc_count: number;
|
||||
};
|
||||
system_module: {
|
||||
login_count: {
|
||||
doc_count: number;
|
||||
};
|
||||
package_count: {
|
||||
doc_count: number;
|
||||
};
|
||||
process_count: {
|
||||
doc_count: number;
|
||||
};
|
||||
user_count: {
|
||||
doc_count: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -13,6 +13,7 @@ import { IndexFields } from './index_fields';
|
|||
import { IpOverview } from './ip_overview';
|
||||
import { KpiNetwork } from './kpi_network';
|
||||
import { Network } from './network';
|
||||
import { Overview } from './overview';
|
||||
import { SourceStatus } from './source_status';
|
||||
import { SourceConfigurations, Sources } from './sources';
|
||||
import { UncommonProcesses } from './uncommon_processes';
|
||||
|
@ -27,6 +28,7 @@ export interface AppDomainLibs {
|
|||
ipOverview: IpOverview;
|
||||
network: Network;
|
||||
kpiNetwork: KpiNetwork;
|
||||
overview: Overview;
|
||||
uncommonProcesses: UncommonProcesses;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,5 +17,7 @@ export default function ({ loadTestFile }) {
|
|||
loadTestFile(require.resolve('./timeline_details'));
|
||||
loadTestFile(require.resolve('./uncommon_processes'));
|
||||
loadTestFile(require.resolve('./kpi_network'));
|
||||
loadTestFile(require.resolve('./overview_network'));
|
||||
loadTestFile(require.resolve('./overview_host'));
|
||||
});
|
||||
}
|
||||
|
|
55
x-pack/test/api_integration/apis/secops/overview_host.ts
Normal file
55
x-pack/test/api_integration/apis/secops/overview_host.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { overviewHostQuery } from '../../../../plugins/secops/public/containers/overview/overview_host/index.gql_query';
|
||||
import { GetOverviewHostQuery } from '../../../../plugins/secops/public/graphql/types';
|
||||
import { KbnTestProvider } from './types';
|
||||
|
||||
const overviewHostTests: KbnTestProvider = ({ getService }) => {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const client = getService('secOpsGraphQLClient');
|
||||
describe('Overview Host', () => {
|
||||
describe('With auditbeat', () => {
|
||||
before(() => esArchiver.load('auditbeat/overview'));
|
||||
after(() => esArchiver.unload('auditbeat/overview'));
|
||||
|
||||
const FROM = new Date('2000-01-01T00:00:00.000Z').valueOf();
|
||||
const TO = new Date('3000-01-01T00:00:00.000Z').valueOf();
|
||||
const expectedResult = {
|
||||
auditbeatAuditd: 2194,
|
||||
auditbeatFIM: 4,
|
||||
auditbeatLogin: 2810,
|
||||
auditbeatPackage: 3,
|
||||
auditbeatProcess: 7,
|
||||
auditbeatUser: 6,
|
||||
__typename: 'OverviewHostData',
|
||||
};
|
||||
|
||||
it('Make sure that we get OverviewHost data', () => {
|
||||
return client
|
||||
.query<GetOverviewHostQuery.Query>({
|
||||
query: overviewHostQuery,
|
||||
variables: {
|
||||
sourceId: 'default',
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
to: TO,
|
||||
from: FROM,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(resp => {
|
||||
const overviewHost = resp.data.source.OverviewHost;
|
||||
expect(overviewHost).to.eql(expectedResult);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// tslint:disable-next-line no-default-export
|
||||
export default overviewHostTests;
|
125
x-pack/test/api_integration/apis/secops/overview_network.ts
Normal file
125
x-pack/test/api_integration/apis/secops/overview_network.ts
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { overviewNetworkQuery } from '../../../../plugins/secops/public/containers/overview/overview_network/index.gql_query';
|
||||
import { GetOverviewNetworkQuery } from '../../../../plugins/secops/public/graphql/types';
|
||||
import { KbnTestProvider } from './types';
|
||||
|
||||
const overviewNetworkTests: KbnTestProvider = ({ getService }) => {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const client = getService('secOpsGraphQLClient');
|
||||
describe('Overview Network', () => {
|
||||
describe('With filebeat', () => {
|
||||
before(() => esArchiver.load('filebeat/default'));
|
||||
after(() => esArchiver.unload('filebeat/default'));
|
||||
|
||||
const FROM = new Date('2000-01-01T00:00:00.000Z').valueOf();
|
||||
const TO = new Date('3000-01-01T00:00:00.000Z').valueOf();
|
||||
|
||||
const expectedResult = {
|
||||
packetbeatFlow: 0,
|
||||
packetbeatDNS: 0,
|
||||
filebeatSuricata: 4547,
|
||||
filebeatZeek: 0,
|
||||
auditbeatSocket: 0,
|
||||
__typename: 'OverviewNetworkData',
|
||||
};
|
||||
|
||||
it('Make sure that we get OverviewNetwork data', () => {
|
||||
return client
|
||||
.query<GetOverviewNetworkQuery.Query>({
|
||||
query: overviewNetworkQuery,
|
||||
variables: {
|
||||
sourceId: 'default',
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
to: TO,
|
||||
from: FROM,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(resp => {
|
||||
const overviewNetwork = resp.data.source.OverviewNetwork;
|
||||
expect(overviewNetwork).to.eql(expectedResult);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('With packetbeat', () => {
|
||||
before(() => esArchiver.load('packetbeat/overview'));
|
||||
after(() => esArchiver.unload('packetbeat/overview'));
|
||||
|
||||
const FROM = new Date('2000-01-01T00:00:00.000Z').valueOf();
|
||||
const TO = new Date('3000-01-01T00:00:00.000Z').valueOf();
|
||||
const expectedResult = {
|
||||
packetbeatFlow: 0,
|
||||
packetbeatDNS: 0,
|
||||
filebeatSuricata: 4547,
|
||||
filebeatZeek: 0,
|
||||
auditbeatSocket: 0,
|
||||
__typename: 'OverviewNetworkData',
|
||||
};
|
||||
|
||||
it('Make sure that we get OverviewNetwork data', () => {
|
||||
return client
|
||||
.query<GetOverviewNetworkQuery.Query>({
|
||||
query: overviewNetworkQuery,
|
||||
variables: {
|
||||
sourceId: 'default',
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
to: TO,
|
||||
from: FROM,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(resp => {
|
||||
const overviewNetwork = resp.data.source.OverviewNetwork;
|
||||
expect(overviewNetwork).to.eql(expectedResult);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('With auditbeat', () => {
|
||||
before(() => esArchiver.load('auditbeat/overview'));
|
||||
after(() => esArchiver.unload('auditbeat/overview'));
|
||||
|
||||
const FROM = new Date('2000-01-01T00:00:00.000Z').valueOf();
|
||||
const TO = new Date('3000-01-01T00:00:00.000Z').valueOf();
|
||||
const expectedResult = {
|
||||
packetbeatFlow: 0,
|
||||
packetbeatDNS: 0,
|
||||
filebeatSuricata: 4547,
|
||||
filebeatZeek: 0,
|
||||
auditbeatSocket: 0,
|
||||
__typename: 'OverviewNetworkData',
|
||||
};
|
||||
|
||||
it('Make sure that we get OverviewNetwork data', () => {
|
||||
return client
|
||||
.query<GetOverviewNetworkQuery.Query>({
|
||||
query: overviewNetworkQuery,
|
||||
variables: {
|
||||
sourceId: 'default',
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
to: TO,
|
||||
from: FROM,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(resp => {
|
||||
const overviewNetwork = resp.data.source.OverviewNetwork;
|
||||
expect(overviewNetwork).to.eql(expectedResult);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// tslint:disable-next-line no-default-export
|
||||
export default overviewNetworkTests;
|
Binary file not shown.
3117
x-pack/test/functional/es_archives/auditbeat/overview/mappings.json
Normal file
3117
x-pack/test/functional/es_archives/auditbeat/overview/mappings.json
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
3288
x-pack/test/functional/es_archives/packetbeat/overview/mappings.json
Normal file
3288
x-pack/test/functional/es_archives/packetbeat/overview/mappings.json
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue