From d3fc2ff166729c4f4a6311ccee51bde2d6e4790f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Wed, 23 Sep 2020 17:48:31 +0200 Subject: [PATCH 01/35] [Security Solution] Cleanup Uncommon Processes graphql (#78271) --- .../hosts/uncommon_processes/index.ts | 45 +-- .../security_solution/index.ts | 8 +- .../public/graphql/introspection.json | 234 ---------------- .../security_solution/public/graphql/types.ts | 144 ---------- .../uncommon_process_table/index.test.tsx | 108 +++---- .../uncommon_process_table/index.tsx | 21 +- .../components/uncommon_process_table/mock.ts | 187 ++++++------ .../uncommon_processes/index.gql_query.ts | 59 ---- .../containers/uncommon_processes/index.tsx | 23 +- .../security_solution/server/graphql/index.ts | 2 - .../security_solution/server/graphql/types.ts | 151 ---------- .../graphql/uncommon_processes/index.ts | 8 - .../graphql/uncommon_processes/resolvers.ts | 35 --- .../graphql/uncommon_processes/schema.gql.ts | 39 --- .../security_solution/server/init_server.ts | 2 - .../server/lib/compose/kibana.ts | 2 - .../security_solution/server/lib/types.ts | 2 - .../elasticsearch_adapter.test.ts | 265 ------------------ .../elasticsearch_adapter.ts | 118 -------- .../server/lib/uncommon_processes/index.ts | 21 -- .../lib/uncommon_processes/query.dsl.ts | 222 --------------- .../server/lib/uncommon_processes/types.ts | 54 ---- .../hosts/uncommon_processes/helpers.test.ts | 12 +- .../hosts/uncommon_processes/helpers.ts | 14 +- .../hosts/uncommon_processes/index.test.ts | 4 +- .../factory/hosts/uncommon_processes/index.ts | 10 +- .../apis/security_solution/index.js | 2 +- .../security_solution/uncommon_processes.ts | 2 + 28 files changed, 192 insertions(+), 1602 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.gql_query.ts delete mode 100644 x-pack/plugins/security_solution/server/graphql/uncommon_processes/index.ts delete mode 100644 x-pack/plugins/security_solution/server/graphql/uncommon_processes/resolvers.ts delete mode 100644 x-pack/plugins/security_solution/server/graphql/uncommon_processes/schema.gql.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/uncommon_processes/elasticsearch_adapter.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/uncommon_processes/elasticsearch_adapter.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/uncommon_processes/index.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/uncommon_processes/query.dsl.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/uncommon_processes/types.ts diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/uncommon_processes/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/uncommon_processes/index.ts index 28c0ccb7f6f4f5..19fc9333de7a4e 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/uncommon_processes/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/uncommon_processes/index.ts @@ -7,6 +7,7 @@ import { IEsSearchResponse } from '../../../../../../../../src/plugins/data/comm import { HostEcs } from '../../../../ecs/host'; import { UserEcs } from '../../../../ecs/user'; +import { ProcessEcs } from '../../../../ecs/process'; import { RequestOptionsPaginated, SortField, @@ -20,56 +21,32 @@ import { Hits, } from '../../..'; -export interface HostUncommonProcessesRequestOptions extends RequestOptionsPaginated { +export interface HostsUncommonProcessesRequestOptions extends RequestOptionsPaginated { sort: SortField; defaultIndex: string[]; } -export interface HostUncommonProcessesStrategyResponse extends IEsSearchResponse { - edges: UncommonProcessesEdges[]; +export interface HostsUncommonProcessesStrategyResponse extends IEsSearchResponse { + edges: HostsUncommonProcessesEdges[]; totalCount: number; pageInfo: PageInfoPaginated; inspect?: Maybe; } -export interface UncommonProcessesEdges { - node: UncommonProcessItem; +export interface HostsUncommonProcessesEdges { + node: HostsUncommonProcessItem; cursor: CursorType; } -export interface UncommonProcessItem { +export interface HostsUncommonProcessItem { _id: string; instances: number; - process: ProcessEcsFields; + process: ProcessEcs; hosts: HostEcs[]; user?: Maybe; } -export interface ProcessEcsFields { - hash?: Maybe; - pid?: Maybe; - name?: Maybe; - ppid?: Maybe; - args?: Maybe; - entity_id?: Maybe; - executable?: Maybe; - title?: Maybe; - thread?: Maybe; - working_directory?: Maybe; -} - -export interface ProcessHashData { - md5?: Maybe; - sha1?: Maybe; - sha256?: Maybe; -} - -export interface Thread { - id?: Maybe; - start?: Maybe; -} - -export interface UncommonProcessHit extends Hit { +export interface HostsUncommonProcessHit extends Hit { total: TotalHit; host: Array<{ id: string[] | undefined; @@ -77,10 +54,10 @@ export interface UncommonProcessHit extends Hit { }>; _source: { '@timestamp': string; - process: ProcessEcsFields; + process: ProcessEcs; }; cursor: string; sort: StringOrNumber[]; } -export type ProcessHits = Hits; +export type ProcessHits = Hits; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts index cfcf613b662bca..af9faef89af466 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts @@ -18,8 +18,8 @@ import { HostsQueries, HostsRequestOptions, HostsStrategyResponse, - HostUncommonProcessesStrategyResponse, - HostUncommonProcessesRequestOptions, + HostsUncommonProcessesStrategyResponse, + HostsUncommonProcessesRequestOptions, HostsKpiQueries, HostsKpiAuthenticationsStrategyResponse, HostsKpiAuthenticationsRequestOptions, @@ -113,7 +113,7 @@ export type StrategyResponseType = T extends HostsQ : T extends HostsQueries.firstLastSeen ? HostFirstLastSeenStrategyResponse : T extends HostsQueries.uncommonProcesses - ? HostUncommonProcessesStrategyResponse + ? HostsUncommonProcessesStrategyResponse : T extends HostsKpiQueries.kpiAuthentications ? HostsKpiAuthenticationsStrategyResponse : T extends HostsKpiQueries.kpiHosts @@ -161,7 +161,7 @@ export type StrategyRequestType = T extends HostsQu : T extends HostsQueries.firstLastSeen ? HostFirstLastSeenRequestOptions : T extends HostsQueries.uncommonProcesses - ? HostUncommonProcessesRequestOptions + ? HostsUncommonProcessesRequestOptions : T extends HostsKpiQueries.kpiAuthentications ? HostsKpiAuthenticationsRequestOptions : T extends HostsKpiQueries.kpiHosts diff --git a/x-pack/plugins/security_solution/public/graphql/introspection.json b/x-pack/plugins/security_solution/public/graphql/introspection.json index 0bbc1fcc80e922..9e6a4f21ec64f5 100644 --- a/x-pack/plugins/security_solution/public/graphql/introspection.json +++ b/x-pack/plugins/security_solution/public/graphql/introspection.json @@ -2186,67 +2186,6 @@ "isDeprecated": false, "deprecationReason": null }, - { - "name": "UncommonProcesses", - "description": "Gets UncommonProcesses based on a timerange, or all UncommonProcesses if no criteria is specified", - "args": [ - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "pagination", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "PaginationInputPaginated", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "defaultIndex", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "UncommonProcessesData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "whoAmI", "description": "Just a simple example to get the app name", @@ -9347,179 +9286,6 @@ "enumValues": null, "possibleTypes": null }, - { - "kind": "OBJECT", - "name": "UncommonProcessesData", - "description": "", - "fields": [ - { - "name": "edges", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "UncommonProcessesEdges", "ofType": null } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "totalCount", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "pageInfo", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "PageInfoPaginated", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UncommonProcessesEdges", - "description": "", - "fields": [ - { - "name": "node", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "UncommonProcessItem", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "cursor", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UncommonProcessItem", - "description": "", - "fields": [ - { - "name": "_id", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "instances", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "process", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "ProcessEcsFields", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "hosts", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "HostEcsFields", "ofType": null } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "UserEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, { "kind": "OBJECT", "name": "SayMyName", diff --git a/x-pack/plugins/security_solution/public/graphql/types.ts b/x-pack/plugins/security_solution/public/graphql/types.ts index 4d3837f434b053..1699ac4dd33eb9 100644 --- a/x-pack/plugins/security_solution/public/graphql/types.ts +++ b/x-pack/plugins/security_solution/public/graphql/types.ts @@ -558,8 +558,6 @@ export interface Source { OverviewNetwork?: Maybe; OverviewHost?: Maybe; - /** 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 */ whoAmI?: Maybe; } @@ -1916,34 +1914,6 @@ export interface OverviewHostData { inspect?: Maybe; } -export interface UncommonProcessesData { - edges: UncommonProcessesEdges[]; - - totalCount: number; - - pageInfo: PageInfoPaginated; - - inspect?: Maybe; -} - -export interface UncommonProcessesEdges { - node: UncommonProcessItem; - - cursor: CursorType; -} - -export interface UncommonProcessItem { - _id: string; - - instances: number; - - process: ProcessEcsFields; - - hosts: HostEcsFields[]; - - user?: Maybe; -} - export interface SayMyName { /** The id of the source */ appName: string; @@ -2531,15 +2501,6 @@ export interface OverviewHostSourceArgs { defaultIndex: string[]; } -export interface UncommonProcessesSourceArgs { - timerange: TimerangeInput; - - pagination: PaginationInputPaginated; - - filterQuery?: Maybe; - - defaultIndex: string[]; -} export interface IndicesExistSourceStatusArgs { defaultIndex: string[]; } @@ -3241,111 +3202,6 @@ export namespace GetKpiHostsQuery { }; } -export namespace GetUncommonProcessesQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - pagination: PaginationInputPaginated; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - UncommonProcesses: UncommonProcesses; - }; - - export type UncommonProcesses = { - __typename?: 'UncommonProcessesData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'UncommonProcessesEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'UncommonProcessItem'; - - _id: string; - - instances: number; - - process: Process; - - user: Maybe; - - hosts: Hosts[]; - }; - - export type Process = { - __typename?: 'ProcessEcsFields'; - - args: Maybe; - - name: Maybe; - }; - - export type User = { - __typename?: 'UserEcsFields'; - - id: Maybe; - - name: Maybe; - }; - - export type Hosts = { - __typename?: 'HostEcsFields'; - - name: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - export namespace GetIpOverviewQuery { export type Variables = { sourceId: string; diff --git a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx index 5ace3439a2de69..41f443f14cafe8 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx @@ -30,18 +30,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = shallow( @@ -54,18 +50,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -79,18 +71,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -105,18 +93,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -131,18 +115,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -157,18 +137,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -183,18 +159,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -208,18 +180,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -233,18 +201,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.tsx index 31d7fb10edb1c3..c7025bb489ae4e 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.tsx @@ -9,7 +9,10 @@ import React, { useCallback, useMemo } from 'react'; import { connect, ConnectedProps } from 'react-redux'; -import { UncommonProcessesEdges, UncommonProcessItem } from '../../../graphql/types'; +import { + HostsUncommonProcessesEdges, + HostsUncommonProcessItem, +} from '../../../../common/search_strategy'; import { State } from '../../../common/store'; import { hostsActions, hostsModel, hostsSelectors } from '../../store'; import { defaultToEmptyTag, getEmptyValue } from '../../../common/components/empty_value'; @@ -21,7 +24,7 @@ import { getRowItemDraggables } from '../../../common/components/tables/helpers' import { HostsType } from '../../store/model'; const tableType = hostsModel.HostsTableType.uncommonProcesses; interface OwnProps { - data: UncommonProcessesEdges[]; + data: HostsUncommonProcessesEdges[]; fakeTotalCount: number; id: string; isInspect: boolean; @@ -33,12 +36,12 @@ interface OwnProps { } export type UncommonProcessTableColumns = [ - Columns, - Columns, - Columns, - Columns, - Columns, - Columns + Columns, + Columns, + Columns, + Columns, + Columns, + Columns ]; type UncommonProcessTableProps = OwnProps & PropsFromRedux; @@ -212,7 +215,7 @@ const getUncommonColumns = (): UncommonProcessTableColumns => [ }, ]; -export const getHostNames = (node: UncommonProcessItem): string[] => { +export const getHostNames = (node: HostsUncommonProcessItem): string[] => { if (node.hosts != null) { return node.hosts .filter((host) => host.name != null && host.name[0] != null) diff --git a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/mock.ts b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/mock.ts index 52b835278634b0..56853c1bfaae1d 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/mock.ts +++ b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/mock.ts @@ -4,116 +4,115 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UncommonProcessesData } from '../../../graphql/types'; +import { HostsUncommonProcessesStrategyResponse } from '../../../../common/search_strategy'; -export const mockData: { UncommonProcess: UncommonProcessesData } = { - UncommonProcess: { - totalCount: 5, - edges: [ - { - node: { - _id: 'cPsuhGcB0WOhS6qyTKC0', - process: { - title: ['Hello World'], - name: ['elrond.elstc.co'], - }, - hosts: [], - instances: 93, - user: { - id: ['0'], - name: ['root'], - }, +export const mockData: HostsUncommonProcessesStrategyResponse = { + totalCount: 5, + edges: [ + { + node: { + _id: 'cPsuhGcB0WOhS6qyTKC0', + process: { + title: ['Hello World'], + name: ['elrond.elstc.co'], }, - cursor: { - value: '98966fa2013c396155c460d35c0902be', + hosts: [], + instances: 93, + user: { + id: ['0'], + name: ['root'], }, }, - { - node: { - _id: 'cPsuhGcB0WOhS6qyTKC0', - process: { - title: ['Hello World'], - name: ['elrond.elstc.co'], - }, - hosts: [{ id: ['host-id-1'], name: ['hello-world'] }], - instances: 93, - user: { - id: ['0'], - name: ['root'], - }, + cursor: { + value: '98966fa2013c396155c460d35c0902be', + }, + }, + { + node: { + _id: 'cPsuhGcB0WOhS6qyTKC0', + process: { + title: ['Hello World'], + name: ['elrond.elstc.co'], }, - cursor: { - value: '98966fa2013c396155c460d35c0902be', + hosts: [{ id: ['host-id-1'], name: ['hello-world'] }], + instances: 93, + user: { + id: ['0'], + name: ['root'], }, }, - { - node: { - _id: 'KwQDiWcB0WOhS6qyXmrW', - process: { - title: ['Hello World'], - name: ['siem-kibana'], - }, - hosts: [ - { id: ['host-id-1'], name: ['hello-world'] }, - { id: ['host-id-2'], name: ['hello-world-2'] }, - ], - instances: 97, - user: { - id: ['1'], - name: ['Evan'], - }, + cursor: { + value: '98966fa2013c396155c460d35c0902be', + }, + }, + { + node: { + _id: 'KwQDiWcB0WOhS6qyXmrW', + process: { + title: ['Hello World'], + name: ['siem-kibana'], }, - cursor: { - value: 'aa7ca589f1b8220002f2fc61c64cfbf1', + hosts: [ + { id: ['host-id-1'], name: ['hello-world'] }, + { id: ['host-id-2'], name: ['hello-world-2'] }, + ], + instances: 97, + user: { + id: ['1'], + name: ['Evan'], }, }, - { - node: { - _id: 'KwQDiWcB0WOhS6qyXmrW', - process: { - title: ['Hello World'], - name: ['siem-kibana'], - }, - hosts: [{ ip: ['127.0.0.1'] }], - instances: 97, - user: { - id: ['1'], - name: ['Evan'], - }, + cursor: { + value: 'aa7ca589f1b8220002f2fc61c64cfbf1', + }, + }, + { + node: { + _id: 'KwQDiWcB0WOhS6qyXmrW', + process: { + title: ['Hello World'], + name: ['siem-kibana'], }, - cursor: { - value: 'aa7ca589f1b8220002f2fc61c64cfbf1', + hosts: [{ ip: ['127.0.0.1'] }], + instances: 97, + user: { + id: ['1'], + name: ['Evan'], }, }, - { - node: { - _id: 'KwQDiWcB0WOhS6qyXmrW', - process: { - title: ['Hello World'], - name: ['siem-kibana'], - }, - hosts: [ - { ip: ['127.0.0.1'] }, - { id: ['host-id-1'], name: ['hello-world'] }, - { ip: ['127.0.0.1'] }, - { id: ['host-id-2'], name: ['hello-world-2'] }, - { ip: ['127.0.0.1'] }, - ], - instances: 97, - user: { - id: ['1'], - name: ['Evan'], - }, + cursor: { + value: 'aa7ca589f1b8220002f2fc61c64cfbf1', + }, + }, + { + node: { + _id: 'KwQDiWcB0WOhS6qyXmrW', + process: { + title: ['Hello World'], + name: ['siem-kibana'], }, - cursor: { - value: 'aa7ca589f1b8220002f2fc61c64cfbf1', + hosts: [ + { ip: ['127.0.0.1'] }, + { id: ['host-id-1'], name: ['hello-world'] }, + { ip: ['127.0.0.1'] }, + { id: ['host-id-2'], name: ['hello-world-2'] }, + { ip: ['127.0.0.1'] }, + ], + instances: 97, + user: { + id: ['1'], + name: ['Evan'], }, }, - ], - pageInfo: { - activePage: 1, - fakeTotalCount: 50, - showMorePagesIndicator: true, + cursor: { + value: 'aa7ca589f1b8220002f2fc61c64cfbf1', + }, }, + ], + pageInfo: { + activePage: 1, + fakeTotalCount: 50, + showMorePagesIndicator: true, }, + rawResponse: {} as HostsUncommonProcessesStrategyResponse['rawResponse'], }; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.gql_query.ts b/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.gql_query.ts deleted file mode 100644 index d984de020faa1e..00000000000000 --- a/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.gql_query.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -export const uncommonProcessesQuery = gql` - query GetUncommonProcessesQuery( - $sourceId: ID! - $timerange: TimerangeInput! - $pagination: PaginationInputPaginated! - $filterQuery: String - $defaultIndex: [String!]! - $inspect: Boolean! - ) { - source(id: $sourceId) { - id - UncommonProcesses( - timerange: $timerange - pagination: $pagination - filterQuery: $filterQuery - defaultIndex: $defaultIndex - ) { - totalCount - edges { - node { - _id - instances - process { - args - name - } - user { - id - name - } - hosts { - name - } - } - cursor { - value - } - } - pageInfo { - activePage - fakeTotalCount - showMorePagesIndicator - } - inspect @include(if: $inspect) { - dsl - response - } - } - } - } -`; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx index ae4ea83f887253..e28a808378dd74 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx @@ -16,19 +16,20 @@ import { } from '../../../../../../../src/plugins/data/common'; import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; -import { PageInfoPaginated, UncommonProcessesEdges } from '../../../graphql/types'; import { inputsModel, State } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; import { generateTablePaginationOptions } from '../../../common/components/paginated_table/helpers'; import { createFilter } from '../../../common/containers/helpers'; - import { hostsModel, hostsSelectors } from '../../store'; import { - HostUncommonProcessesRequestOptions, - HostUncommonProcessesStrategyResponse, -} from '../../../../common/search_strategy/security_solution/hosts/uncommon_processes'; -import { HostsQueries } from '../../../../common/search_strategy/security_solution/hosts'; -import { DocValueFields, SortField } from '../../../../common/search_strategy'; + DocValueFields, + SortField, + PageInfoPaginated, + HostsUncommonProcessesEdges, + HostsQueries, + HostsUncommonProcessesRequestOptions, + HostsUncommonProcessesStrategyResponse, +} from '../../../../common/search_strategy'; import * as i18n from './translations'; import { ESTermQuery } from '../../../../common/typed_json'; @@ -45,7 +46,7 @@ export interface UncommonProcessesArgs { pageInfo: PageInfoPaginated; refetch: inputsModel.Refetch; totalCount: number; - uncommonProcesses: UncommonProcessesEdges[]; + uncommonProcesses: HostsUncommonProcessesEdges[]; } interface UseUncommonProcesses { @@ -75,7 +76,7 @@ export const useUncommonProcesses = ({ const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [uncommonProcessesRequest, setUncommonProcessesRequest] = useState< - HostUncommonProcessesRequestOptions + HostsUncommonProcessesRequestOptions >({ defaultIndex, docValueFields: docValueFields ?? [], @@ -123,14 +124,14 @@ export const useUncommonProcesses = ({ ); const uncommonProcessesSearch = useCallback( - (request: HostUncommonProcessesRequestOptions) => { + (request: HostsUncommonProcessesRequestOptions) => { let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); const searchSubscription$ = data.search - .search( + .search( request, { strategy: 'securitySolutionSearchStrategy', diff --git a/x-pack/plugins/security_solution/server/graphql/index.ts b/x-pack/plugins/security_solution/server/graphql/index.ts index 959aa4549d43f8..e949150c47c6cf 100644 --- a/x-pack/plugins/security_solution/server/graphql/index.ts +++ b/x-pack/plugins/security_solution/server/graphql/index.ts @@ -26,7 +26,6 @@ import { toNumberSchema } from './scalar_to_number_array'; import { sourceStatusSchema } from './source_status'; import { sourcesSchema } from './sources'; import { timelineSchema } from './timeline'; -import { uncommonProcessesSchema } from './uncommon_processes'; import { whoAmISchema } from './who_am_i'; import { matrixHistogramSchema } from './matrix_histogram'; export const schemas = [ @@ -52,6 +51,5 @@ export const schemas = [ sourceStatusSchema, sharedSchema, timelineSchema, - uncommonProcessesSchema, whoAmISchema, ]; diff --git a/x-pack/plugins/security_solution/server/graphql/types.ts b/x-pack/plugins/security_solution/server/graphql/types.ts index ed3abd25df882e..5887feb63c2a1b 100644 --- a/x-pack/plugins/security_solution/server/graphql/types.ts +++ b/x-pack/plugins/security_solution/server/graphql/types.ts @@ -560,8 +560,6 @@ export interface Source { OverviewNetwork?: Maybe; OverviewHost?: Maybe; - /** 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 */ whoAmI?: Maybe; } @@ -1918,34 +1916,6 @@ export interface OverviewHostData { inspect?: Maybe; } -export interface UncommonProcessesData { - edges: UncommonProcessesEdges[]; - - totalCount: number; - - pageInfo: PageInfoPaginated; - - inspect?: Maybe; -} - -export interface UncommonProcessesEdges { - node: UncommonProcessItem; - - cursor: CursorType; -} - -export interface UncommonProcessItem { - _id: string; - - instances: number; - - process: ProcessEcsFields; - - hosts: HostEcsFields[]; - - user?: Maybe; -} - export interface SayMyName { /** The id of the source */ appName: string; @@ -2533,15 +2503,6 @@ export interface OverviewHostSourceArgs { defaultIndex: string[]; } -export interface UncommonProcessesSourceArgs { - timerange: TimerangeInput; - - pagination: PaginationInputPaginated; - - filterQuery?: Maybe; - - defaultIndex: string[]; -} export interface IndicesExistSourceStatusArgs { defaultIndex: string[]; } @@ -2982,8 +2943,6 @@ export namespace SourceResolvers { OverviewNetwork?: OverviewNetworkResolver, TypeParent, TContext>; OverviewHost?: OverviewHostResolver, TypeParent, TContext>; - /** Gets UncommonProcesses based on a timerange, or all UncommonProcesses if no criteria is specified */ - UncommonProcesses?: UncommonProcessesResolver; /** Just a simple example to get the app name */ whoAmI?: WhoAmIResolver, TypeParent, TContext>; } @@ -3365,21 +3324,6 @@ export namespace SourceResolvers { defaultIndex: string[]; } - export type UncommonProcessesResolver< - R = UncommonProcessesData, - Parent = Source, - TContext = SiemContext - > = Resolver; - export interface UncommonProcessesArgs { - timerange: TimerangeInput; - - pagination: PaginationInputPaginated; - - filterQuery?: Maybe; - - defaultIndex: string[]; - } - export type WhoAmIResolver< R = Maybe, Parent = Source, @@ -7936,98 +7880,6 @@ export namespace OverviewHostDataResolvers { > = Resolver; } -export namespace UncommonProcessesDataResolvers { - export interface Resolvers { - edges?: EdgesResolver; - - totalCount?: TotalCountResolver; - - pageInfo?: PageInfoResolver; - - inspect?: InspectResolver, TypeParent, TContext>; - } - - export type EdgesResolver< - R = UncommonProcessesEdges[], - Parent = UncommonProcessesData, - TContext = SiemContext - > = Resolver; - export type TotalCountResolver< - R = number, - Parent = UncommonProcessesData, - TContext = SiemContext - > = Resolver; - export type PageInfoResolver< - R = PageInfoPaginated, - Parent = UncommonProcessesData, - TContext = SiemContext - > = Resolver; - export type InspectResolver< - R = Maybe, - Parent = UncommonProcessesData, - TContext = SiemContext - > = Resolver; -} - -export namespace UncommonProcessesEdgesResolvers { - export interface Resolvers { - node?: NodeResolver; - - cursor?: CursorResolver; - } - - export type NodeResolver< - R = UncommonProcessItem, - Parent = UncommonProcessesEdges, - TContext = SiemContext - > = Resolver; - export type CursorResolver< - R = CursorType, - Parent = UncommonProcessesEdges, - TContext = SiemContext - > = Resolver; -} - -export namespace UncommonProcessItemResolvers { - export interface Resolvers { - _id?: _IdResolver; - - instances?: InstancesResolver; - - process?: ProcessResolver; - - hosts?: HostsResolver; - - user?: UserResolver, TypeParent, TContext>; - } - - export type _IdResolver< - R = string, - Parent = UncommonProcessItem, - TContext = SiemContext - > = Resolver; - export type InstancesResolver< - R = number, - Parent = UncommonProcessItem, - TContext = SiemContext - > = Resolver; - export type ProcessResolver< - R = ProcessEcsFields, - Parent = UncommonProcessItem, - TContext = SiemContext - > = Resolver; - export type HostsResolver< - R = HostEcsFields[], - Parent = UncommonProcessItem, - TContext = SiemContext - > = Resolver; - export type UserResolver< - R = Maybe, - Parent = UncommonProcessItem, - TContext = SiemContext - > = Resolver; -} - export namespace SayMyNameResolvers { export interface Resolvers { /** The id of the source */ @@ -9308,9 +9160,6 @@ export type IResolvers = { NetworkHttpItem?: NetworkHttpItemResolvers.Resolvers; OverviewNetworkData?: OverviewNetworkDataResolvers.Resolvers; OverviewHostData?: OverviewHostDataResolvers.Resolvers; - UncommonProcessesData?: UncommonProcessesDataResolvers.Resolvers; - UncommonProcessesEdges?: UncommonProcessesEdgesResolvers.Resolvers; - UncommonProcessItem?: UncommonProcessItemResolvers.Resolvers; SayMyName?: SayMyNameResolvers.Resolvers; TimelineResult?: TimelineResultResolvers.Resolvers; ColumnHeaderResult?: ColumnHeaderResultResolvers.Resolvers; diff --git a/x-pack/plugins/security_solution/server/graphql/uncommon_processes/index.ts b/x-pack/plugins/security_solution/server/graphql/uncommon_processes/index.ts deleted file mode 100644 index d0da0efd8a5604..00000000000000 --- a/x-pack/plugins/security_solution/server/graphql/uncommon_processes/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { createUncommonProcessesResolvers } from './resolvers'; -export { uncommonProcessesSchema } from './schema.gql'; diff --git a/x-pack/plugins/security_solution/server/graphql/uncommon_processes/resolvers.ts b/x-pack/plugins/security_solution/server/graphql/uncommon_processes/resolvers.ts deleted file mode 100644 index 03d3c3d1a1fe4f..00000000000000 --- a/x-pack/plugins/security_solution/server/graphql/uncommon_processes/resolvers.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { SourceResolvers } from '../../graphql/types'; -import { AppResolverOf, ChildResolverOf } from '../../lib/framework'; -import { UncommonProcesses } from '../../lib/uncommon_processes'; -import { createOptionsPaginated } from '../../utils/build_query/create_options'; -import { QuerySourceResolver } from '../sources/resolvers'; - -type QueryUncommonProcessesResolver = ChildResolverOf< - AppResolverOf, - QuerySourceResolver ->; - -export interface UncommonProcessesResolversDeps { - uncommonProcesses: UncommonProcesses; -} - -export const createUncommonProcessesResolvers = ( - libs: UncommonProcessesResolversDeps -): { - Source: { - UncommonProcesses: QueryUncommonProcessesResolver; - }; -} => ({ - Source: { - async UncommonProcesses(source, args, { req }, info) { - const options = createOptionsPaginated(source, args, info); - return libs.uncommonProcesses.getUncommonProcesses(req, options); - }, - }, -}); diff --git a/x-pack/plugins/security_solution/server/graphql/uncommon_processes/schema.gql.ts b/x-pack/plugins/security_solution/server/graphql/uncommon_processes/schema.gql.ts deleted file mode 100644 index 36a3da6779172b..00000000000000 --- a/x-pack/plugins/security_solution/server/graphql/uncommon_processes/schema.gql.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -export const uncommonProcessesSchema = gql` - type UncommonProcessItem { - _id: String! - instances: Float! - process: ProcessEcsFields! - hosts: [HostEcsFields!]! - user: UserEcsFields - } - - type UncommonProcessesEdges { - node: UncommonProcessItem! - cursor: CursorType! - } - - type UncommonProcessesData { - edges: [UncommonProcessesEdges!]! - totalCount: Float! - pageInfo: PageInfoPaginated! - inspect: Inspect - } - - extend type Source { - "Gets UncommonProcesses based on a timerange, or all UncommonProcesses if no criteria is specified" - UncommonProcesses( - timerange: TimerangeInput! - pagination: PaginationInputPaginated! - filterQuery: String - defaultIndex: [String!]! - ): UncommonProcessesData! - } -`; diff --git a/x-pack/plugins/security_solution/server/init_server.ts b/x-pack/plugins/security_solution/server/init_server.ts index 2ef42eaee4b986..7cb2127a3d9d7f 100644 --- a/x-pack/plugins/security_solution/server/init_server.ts +++ b/x-pack/plugins/security_solution/server/init_server.ts @@ -25,7 +25,6 @@ import { createScalarToNumberArrayValueResolvers } from './graphql/scalar_to_num import { createSourceStatusResolvers } from './graphql/source_status'; import { createSourcesResolvers } from './graphql/sources'; import { createTimelineResolvers } from './graphql/timeline'; -import { createUncommonProcessesResolvers } from './graphql/uncommon_processes'; import { createWhoAmIResolvers } from './graphql/who_am_i'; import { AppBackendLibs } from './lib/types'; import { createMatrixHistogramResolvers } from './graphql/matrix_histogram'; @@ -54,7 +53,6 @@ export const initServer = (libs: AppBackendLibs) => { createSourcesResolvers(libs) as IResolvers, createSourceStatusResolvers(libs) as IResolvers, createTimelineResolvers(libs) as IResolvers, - createUncommonProcessesResolvers(libs) as IResolvers, createWhoAmIResolvers() as IResolvers, createKpiHostsResolvers(libs) as IResolvers, ], diff --git a/x-pack/plugins/security_solution/server/lib/compose/kibana.ts b/x-pack/plugins/security_solution/server/lib/compose/kibana.ts index bab00e33e33789..430ada93b45149 100644 --- a/x-pack/plugins/security_solution/server/lib/compose/kibana.ts +++ b/x-pack/plugins/security_solution/server/lib/compose/kibana.ts @@ -26,7 +26,6 @@ import { ElasticsearchOverviewAdapter } from '../overview/elasticsearch_adapter' import { ElasticsearchSourceStatusAdapter, SourceStatus } from '../source_status'; import { ConfigurationSourcesAdapter, Sources } from '../sources'; import { AppBackendLibs, AppDomainLibs } from '../types'; -import { ElasticsearchUncommonProcessesAdapter, UncommonProcesses } from '../uncommon_processes'; import * as note from '../note/saved_object'; import * as pinnedEvent from '../pinned_event/saved_object'; import * as timeline from '../timeline/saved_object'; @@ -54,7 +53,6 @@ export function compose( matrixHistogram: new MatrixHistogram(new ElasticsearchMatrixHistogramAdapter(framework)), network: new Network(new ElasticsearchNetworkAdapter(framework)), overview: new Overview(new ElasticsearchOverviewAdapter(framework)), - uncommonProcesses: new UncommonProcesses(new ElasticsearchUncommonProcessesAdapter(framework)), }; const libs: AppBackendLibs = { diff --git a/x-pack/plugins/security_solution/server/lib/types.ts b/x-pack/plugins/security_solution/server/lib/types.ts index 4f70e3aa8652ac..87e755360285fb 100644 --- a/x-pack/plugins/security_solution/server/lib/types.ts +++ b/x-pack/plugins/security_solution/server/lib/types.ts @@ -20,7 +20,6 @@ import { Network } from './network'; import { Overview } from './overview'; import { SourceStatus } from './source_status'; import { Sources } from './sources'; -import { UncommonProcesses } from './uncommon_processes'; import { Note } from './note/saved_object'; import { PinnedEvent } from './pinned_event/saved_object'; import { Timeline } from './timeline/saved_object'; @@ -38,7 +37,6 @@ export interface AppDomainLibs { network: Network; kpiNetwork: KpiNetwork; overview: Overview; - uncommonProcesses: UncommonProcesses; kpiHosts: KpiHosts; } diff --git a/x-pack/plugins/security_solution/server/lib/uncommon_processes/elasticsearch_adapter.test.ts b/x-pack/plugins/security_solution/server/lib/uncommon_processes/elasticsearch_adapter.test.ts deleted file mode 100644 index 2a15f1fe074f81..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/uncommon_processes/elasticsearch_adapter.test.ts +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { UncommonProcessesEdges } from '../../graphql/types'; -import { processFieldsMap } from '../ecs_fields'; - -import { formatUncommonProcessesData, getHosts } from './elasticsearch_adapter'; -import { UncommonProcessBucket, UncommonProcessHit } from './types'; - -describe('elasticsearch_adapter', () => { - describe('#getHosts', () => { - const bucket1: UncommonProcessBucket = { - key: '123', - hosts: { - buckets: [ - { - key: '123', - host: { - hits: { - total: 0, - max_score: 0, - hits: [ - { - _index: 'hit-1', - _type: 'type-1', - _id: 'id-1', - _score: 0, - _source: { - host: { - name: ['host-1'], - id: ['host-id-1'], - }, - }, - }, - ], - }, - }, - }, - ], - }, - process: { - hits: { - total: { - value: 1, - relation: 'eq', - }, - max_score: 5, - hits: [], - }, - }, - }; - const bucket2: UncommonProcessBucket = { - key: '345', - hosts: { - buckets: [ - { - key: '123', - host: { - hits: { - total: 0, - max_score: 0, - hits: [ - { - _index: 'hit-1', - _type: 'type-1', - _id: 'id-1', - _score: 0, - _source: { - host: { - name: ['host-1'], - id: ['host-id-1'], - }, - }, - }, - ], - }, - }, - }, - { - key: '345', - host: { - hits: { - total: 0, - max_score: 0, - hits: [ - { - _index: 'hit-2', - _type: 'type-2', - _id: 'id-2', - _score: 0, - _source: { - host: { - name: ['host-2'], - id: ['host-id-2'], - }, - }, - }, - ], - }, - }, - }, - ], - }, - process: { - hits: { - total: { - value: 1, - relation: 'eq', - }, - max_score: 5, - hits: [], - }, - }, - }; - const bucket3: UncommonProcessBucket = { - key: '789', - hosts: { - buckets: [ - { - key: '789', - host: { - hits: { - total: 0, - max_score: 0, - hits: [ - { - _index: 'hit-9', - _type: 'type-9', - _id: 'id-9', - _score: 0, - _source: { - // @ts-expect-error ts doesn't like seeing the object written this way, but sometimes this is the data we get! - 'host.id': ['host-id-9'], - 'host.name': ['host-9'], - }, - }, - ], - }, - }, - }, - ], - }, - process: { - hits: { - total: { - value: 1, - relation: 'eq', - }, - max_score: 5, - hits: [], - }, - }, - }; - - test('will return a single host correctly', () => { - const hosts = getHosts(bucket1.hosts.buckets); - expect(hosts).toEqual([{ id: ['123'], name: ['host-1'] }]); - }); - - test('will return two hosts correctly', () => { - const hosts = getHosts(bucket2.hosts.buckets); - expect(hosts).toEqual([ - { id: ['123'], name: ['host-1'] }, - { id: ['345'], name: ['host-2'] }, - ]); - }); - - test('will return a dot notation host', () => { - const hosts = getHosts(bucket3.hosts.buckets); - expect(hosts).toEqual([{ id: ['789'], name: ['host-9'] }]); - }); - - test('will return no hosts when given an empty array', () => { - const hosts = getHosts([]); - expect(hosts).toEqual([]); - }); - }); - - describe('#formatUncommonProcessesData', () => { - const hit: UncommonProcessHit = { - _index: 'index-123', - _type: 'type-123', - _id: 'id-123', - _score: 10, - total: { - value: 100, - relation: 'eq', - }, - host: [ - { id: ['host-id-1'], name: ['host-name-1'] }, - { id: ['host-id-1'], name: ['host-name-1'] }, - ], - _source: { - '@timestamp': 'time', - process: { - name: ['process-1'], - title: ['title-1'], - }, - }, - cursor: 'cursor-1', - sort: [0], - }; - - test('it formats a uncommon process data with a source of name correctly', () => { - const fields: readonly string[] = ['process.name']; - const data = formatUncommonProcessesData(fields, hit, processFieldsMap); - const expected: UncommonProcessesEdges = { - cursor: { tiebreaker: null, value: 'cursor-1' }, - node: { - _id: 'id-123', - hosts: [ - { id: ['host-id-1'], name: ['host-name-1'] }, - { id: ['host-id-1'], name: ['host-name-1'] }, - ], - process: { - name: ['process-1'], - }, - instances: 100, - }, - }; - expect(data).toEqual(expected); - }); - - test('it formats a uncommon process data with a source of name and title correctly', () => { - const fields: readonly string[] = ['process.name', 'process.title']; - const data = formatUncommonProcessesData(fields, hit, processFieldsMap); - const expected: UncommonProcessesEdges = { - cursor: { tiebreaker: null, value: 'cursor-1' }, - node: { - _id: 'id-123', - hosts: [ - { id: ['host-id-1'], name: ['host-name-1'] }, - { id: ['host-id-1'], name: ['host-name-1'] }, - ], - instances: 100, - process: { - name: ['process-1'], - title: ['title-1'], - }, - }, - }; - expect(data).toEqual(expected); - }); - - test('it formats a uncommon process data without any data if fields is empty', () => { - const fields: readonly string[] = []; - const data = formatUncommonProcessesData(fields, hit, processFieldsMap); - const expected: UncommonProcessesEdges = { - cursor: { - tiebreaker: null, - value: '', - }, - node: { - _id: '', - hosts: [], - instances: 0, - process: {}, - }, - }; - expect(data).toEqual(expected); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/uncommon_processes/elasticsearch_adapter.ts b/x-pack/plugins/security_solution/server/lib/uncommon_processes/elasticsearch_adapter.ts deleted file mode 100644 index 046823da7cb85f..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/uncommon_processes/elasticsearch_adapter.ts +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { get, getOr } from 'lodash/fp'; - -import { UncommonProcessesData, UncommonProcessesEdges } from '../../graphql/types'; -import { mergeFieldsWithHit, inspectStringifyObject } from '../../utils/build_query'; -import { processFieldsMap, userFieldsMap } from '../ecs_fields'; -import { FrameworkAdapter, FrameworkRequest, RequestOptionsPaginated } from '../framework'; -import { HostHits, TermAggregation } from '../types'; -import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../common/constants'; -import { buildQuery } from './query.dsl'; -import { - UncommonProcessBucket, - UncommonProcessData, - UncommonProcessesAdapter, - UncommonProcessHit, -} from './types'; - -export class ElasticsearchUncommonProcessesAdapter implements UncommonProcessesAdapter { - constructor(private readonly framework: FrameworkAdapter) {} - - public async getUncommonProcesses( - request: FrameworkRequest, - options: RequestOptionsPaginated - ): Promise { - if (options.pagination && options.pagination.querySize >= DEFAULT_MAX_TABLE_QUERY_SIZE) { - throw new Error(`No query size above ${DEFAULT_MAX_TABLE_QUERY_SIZE}`); - } - const dsl = buildQuery(options); - const response = await this.framework.callWithRequest( - request, - 'search', - dsl - ); - const { activePage, cursorStart, fakePossibleCount, querySize } = options.pagination; - const totalCount = getOr(0, 'aggregations.process_count.value', response); - const buckets = getOr([], 'aggregations.group_by_process.buckets', response); - const hits = getHits(buckets); - - const uncommonProcessesEdges = hits.map((hit) => - formatUncommonProcessesData(options.fields, hit, { ...processFieldsMap, ...userFieldsMap }) - ); - - const fakeTotalCount = fakePossibleCount <= totalCount ? fakePossibleCount : totalCount; - const edges = uncommonProcessesEdges.splice(cursorStart, querySize - cursorStart); - const inspect = { - dsl: [inspectStringifyObject(dsl)], - response: [inspectStringifyObject(response)], - }; - - const showMorePagesIndicator = totalCount > fakeTotalCount; - return { - edges, - inspect, - pageInfo: { - activePage: activePage ? activePage : 0, - fakeTotalCount, - showMorePagesIndicator, - }, - totalCount, - }; - } -} - -export const getHits = (buckets: readonly UncommonProcessBucket[]): readonly UncommonProcessHit[] => - buckets.map((bucket: Readonly) => ({ - _id: bucket.process.hits.hits[0]._id, - _index: bucket.process.hits.hits[0]._index, - _type: bucket.process.hits.hits[0]._type, - _score: bucket.process.hits.hits[0]._score, - _source: bucket.process.hits.hits[0]._source, - sort: bucket.process.hits.hits[0].sort, - cursor: bucket.process.hits.hits[0].cursor, - total: bucket.process.hits.total, - host: getHosts(bucket.hosts.buckets), - })); - -export const getHosts = (buckets: ReadonlyArray<{ key: string; host: HostHits }>) => - buckets.map((bucket) => { - const source = get('host.hits.hits[0]._source', bucket); - return { - id: [bucket.key], - name: get('host.name', source), - }; - }); - -export const formatUncommonProcessesData = ( - fields: readonly string[], - hit: UncommonProcessHit, - fieldMap: Readonly> -): UncommonProcessesEdges => - fields.reduce( - (flattenedFields, fieldName) => { - flattenedFields.node._id = hit._id; - flattenedFields.node.instances = getOr(0, 'total.value', hit); - flattenedFields.node.hosts = hit.host; - if (hit.cursor) { - flattenedFields.cursor.value = hit.cursor; - } - return mergeFieldsWithHit(fieldName, flattenedFields, fieldMap, hit); - }, - { - node: { - _id: '', - instances: 0, - process: {}, - hosts: [], - }, - cursor: { - value: '', - tiebreaker: null, - }, - } - ); diff --git a/x-pack/plugins/security_solution/server/lib/uncommon_processes/index.ts b/x-pack/plugins/security_solution/server/lib/uncommon_processes/index.ts deleted file mode 100644 index 0ba0e90f391e1d..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/uncommon_processes/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { UncommonProcessesData } from '../../graphql/types'; -import { FrameworkRequest, RequestOptionsPaginated } from '../framework'; -export * from './elasticsearch_adapter'; -import { UncommonProcessesAdapter } from './types'; - -export class UncommonProcesses { - constructor(private readonly adapter: UncommonProcessesAdapter) {} - - public async getUncommonProcesses( - req: FrameworkRequest, - options: RequestOptionsPaginated - ): Promise { - return this.adapter.getUncommonProcesses(req, options); - } -} diff --git a/x-pack/plugins/security_solution/server/lib/uncommon_processes/query.dsl.ts b/x-pack/plugins/security_solution/server/lib/uncommon_processes/query.dsl.ts deleted file mode 100644 index 4563c769cdc31b..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/uncommon_processes/query.dsl.ts +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { createQueryFilterClauses } from '../../utils/build_query'; -import { reduceFields } from '../../utils/build_query/reduce_fields'; -import { hostFieldsMap, processFieldsMap, userFieldsMap } from '../ecs_fields'; -import { RequestOptionsPaginated } from '../framework'; - -export const buildQuery = ({ - defaultIndex, - fields, - filterQuery, - pagination: { querySize }, - sourceConfiguration: { - fields: { timestamp }, - }, - timerange: { from, to }, -}: RequestOptionsPaginated) => { - const processUserFields = reduceFields(fields, { ...processFieldsMap, ...userFieldsMap }); - const hostFields = reduceFields(fields, hostFieldsMap); - const filter = [ - ...createQueryFilterClauses(filterQuery), - { - range: { - [timestamp]: { - gte: from, - lte: to, - format: 'strict_date_optional_time', - }, - }, - }, - ]; - - const agg = { - process_count: { - cardinality: { - field: 'process.name', - }, - }, - }; - - const dslQuery = { - allowNoIndices: true, - index: defaultIndex, - ignoreUnavailable: true, - body: { - aggregations: { - ...agg, - group_by_process: { - terms: { - size: querySize, - field: 'process.name', - order: [ - { - host_count: 'asc', - }, - { - _count: 'asc', - }, - { - _key: 'asc', - }, - ], - }, - aggregations: { - process: { - top_hits: { - size: 1, - sort: [{ '@timestamp': { order: 'desc' } }], - _source: processUserFields, - }, - }, - host_count: { - cardinality: { - field: 'host.name', - }, - }, - hosts: { - terms: { - field: 'host.name', - }, - aggregations: { - host: { - top_hits: { - size: 1, - _source: hostFields, - }, - }, - }, - }, - }, - }, - }, - query: { - bool: { - should: [ - { - bool: { - filter: [ - { - term: { - 'agent.type': 'auditbeat', - }, - }, - { - term: { - 'event.module': 'auditd', - }, - }, - { - term: { - 'event.action': 'executed', - }, - }, - ], - }, - }, - { - bool: { - filter: [ - { - term: { - 'agent.type': 'auditbeat', - }, - }, - { - term: { - 'event.module': 'system', - }, - }, - { - term: { - 'event.dataset': 'process', - }, - }, - { - term: { - 'event.action': 'process_started', - }, - }, - ], - }, - }, - { - bool: { - filter: [ - { - term: { - 'agent.type': 'winlogbeat', - }, - }, - { - term: { - 'event.code': '4688', - }, - }, - ], - }, - }, - { - bool: { - filter: [ - { - term: { - 'winlog.event_id': 1, - }, - }, - { - term: { - 'winlog.channel': 'Microsoft-Windows-Sysmon/Operational', - }, - }, - ], - }, - }, - { - bool: { - filter: [ - { - term: { - 'event.type': 'process_start', - }, - }, - { - term: { - 'event.category': 'process', - }, - }, - ], - }, - }, - { - bool: { - filter: [ - { - term: { - 'event.category': 'process', - }, - }, - { - term: { - 'event.type': 'start', - }, - }, - ], - }, - }, - ], - minimum_should_match: 1, - filter, - }, - }, - }, - size: 0, - track_total_hits: false, - }; - - return dslQuery; -}; diff --git a/x-pack/plugins/security_solution/server/lib/uncommon_processes/types.ts b/x-pack/plugins/security_solution/server/lib/uncommon_processes/types.ts deleted file mode 100644 index dc60de5963a187..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/uncommon_processes/types.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { ProcessEcsFields, UncommonProcessesData } from '../../graphql/types'; -import { FrameworkRequest, RequestOptionsPaginated } from '../framework'; -import { Hit, Hits, HostHits, SearchHit, TotalHit } from '../types'; - -export interface UncommonProcessesAdapter { - getUncommonProcesses( - req: FrameworkRequest, - options: RequestOptionsPaginated - ): Promise; -} - -type StringOrNumber = string | number; -export interface UncommonProcessHit extends Hit { - total: TotalHit; - host: Array<{ - id: string[] | string | null | undefined; - name: string[] | string | null | undefined; - }>; - _source: { - '@timestamp': string; - process: ProcessEcsFields; - }; - cursor: string; - sort: StringOrNumber[]; -} - -export type ProcessHits = Hits; - -export interface UncommonProcessBucket { - key: string; - hosts: { - buckets: Array<{ key: string; host: HostHits }>; - }; - process: ProcessHits; -} - -export interface UncommonProcessData extends SearchHit { - sort: string[]; - aggregations: { - process_count: { - value: number; - }; - group_by_process: { - after_key: string; - buckets: UncommonProcessBucket[]; - }; - }; -} diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/helpers.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/helpers.test.ts index 096ca570ae852a..a6f44c78e5cc4c 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/helpers.test.ts @@ -7,8 +7,8 @@ import { processFieldsMap } from '../../../../../../common/ecs/ecs_fields'; import { - UncommonProcessesEdges, - UncommonProcessHit, + HostsUncommonProcessesEdges, + HostsUncommonProcessHit, } from '../../../../../../common/search_strategy'; import { formatUncommonProcessesData, getHosts, UncommonProcessBucket } from './helpers'; @@ -183,7 +183,7 @@ describe('helpers', () => { }); describe('#formatUncommonProcessesData', () => { - const hit: UncommonProcessHit = { + const hit: HostsUncommonProcessHit = { _index: 'index-123', _type: 'type-123', _id: 'id-123', @@ -210,7 +210,7 @@ describe('helpers', () => { test('it formats a uncommon process data with a source of name correctly', () => { const fields: readonly string[] = ['process.name']; const data = formatUncommonProcessesData(fields, hit, processFieldsMap); - const expected: UncommonProcessesEdges = { + const expected: HostsUncommonProcessesEdges = { cursor: { tiebreaker: null, value: 'cursor-1' }, node: { _id: 'id-123', @@ -230,7 +230,7 @@ describe('helpers', () => { test('it formats a uncommon process data with a source of name and title correctly', () => { const fields: readonly string[] = ['process.name', 'process.title']; const data = formatUncommonProcessesData(fields, hit, processFieldsMap); - const expected: UncommonProcessesEdges = { + const expected: HostsUncommonProcessesEdges = { cursor: { tiebreaker: null, value: 'cursor-1' }, node: { _id: 'id-123', @@ -251,7 +251,7 @@ describe('helpers', () => { test('it formats a uncommon process data without any data if fields is empty', () => { const fields: readonly string[] = []; const data = formatUncommonProcessesData(fields, hit, processFieldsMap); - const expected: UncommonProcessesEdges = { + const expected: HostsUncommonProcessesEdges = { cursor: { tiebreaker: null, value: '', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/helpers.ts index 5c3d76175b7e4a..20b3f5b05bc87d 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/helpers.ts @@ -9,8 +9,8 @@ import { set } from '@elastic/safer-lodash-set/fp'; import { mergeFieldsWithHit } from '../../../../../utils/build_query'; import { ProcessHits, - UncommonProcessesEdges, - UncommonProcessHit, + HostsUncommonProcessesEdges, + HostsUncommonProcessHit, } from '../../../../../../common/search_strategy/security_solution/hosts/uncommon_processes'; import { toArray } from '../../../../helpers/to_array'; import { HostHits } from '../../../../../../common/search_strategy'; @@ -25,7 +25,9 @@ export const uncommonProcessesFields = [ 'hosts.name', ]; -export const getHits = (buckets: readonly UncommonProcessBucket[]): readonly UncommonProcessHit[] => +export const getHits = ( + buckets: readonly UncommonProcessBucket[] +): readonly HostsUncommonProcessHit[] => buckets.map((bucket: Readonly) => ({ _id: bucket.process.hits.hits[0]._id, _index: bucket.process.hits.hits[0]._index, @@ -57,10 +59,10 @@ export const getHosts = (buckets: ReadonlyArray<{ key: string; host: HostHits }> export const formatUncommonProcessesData = ( fields: readonly string[], - hit: UncommonProcessHit, + hit: HostsUncommonProcessHit, fieldMap: Readonly> -): UncommonProcessesEdges => - fields.reduce( +): HostsUncommonProcessesEdges => + fields.reduce( (flattenedFields, fieldName) => { flattenedFields.node._id = hit._id; flattenedFields.node.instances = getOr(0, 'total.value', hit); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/index.test.ts index a5fa9b459d1bf9..5016c8cc38ce44 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/index.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/index.test.ts @@ -6,7 +6,7 @@ import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../common/constants'; -import { HostUncommonProcessesRequestOptions } from '../../../../../../common/search_strategy/security_solution'; +import { HostsUncommonProcessesRequestOptions } from '../../../../../../common/search_strategy/security_solution'; import * as buildQuery from './dsl/query.dsl'; import { uncommonProcesses } from '.'; import { @@ -35,7 +35,7 @@ describe('uncommonProcesses search strategy', () => { ...mockOptions.pagination, querySize: DEFAULT_MAX_TABLE_QUERY_SIZE, }, - } as HostUncommonProcessesRequestOptions; + } as HostsUncommonProcessesRequestOptions; expect(() => { uncommonProcesses.buildDsl(overSizeOptions); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/index.ts index 5682e63b50ed0c..add2cdb76628ad 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/index.ts @@ -12,8 +12,8 @@ import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../../common/constants import { HostsQueries } from '../../../../../../common/search_strategy/security_solution'; import { processFieldsMap, userFieldsMap } from '../../../../../../common/ecs/ecs_fields'; import { - HostUncommonProcessesRequestOptions, - HostUncommonProcessesStrategyResponse, + HostsUncommonProcessesRequestOptions, + HostsUncommonProcessesStrategyResponse, } from '../../../../../../common/search_strategy/security_solution/hosts/uncommon_processes'; import { inspectStringifyObject } from '../../../../../utils/build_query'; @@ -23,16 +23,16 @@ import { buildQuery } from './dsl/query.dsl'; import { formatUncommonProcessesData, getHits, uncommonProcessesFields } from './helpers'; export const uncommonProcesses: SecuritySolutionFactory = { - buildDsl: (options: HostUncommonProcessesRequestOptions) => { + buildDsl: (options: HostsUncommonProcessesRequestOptions) => { if (options.pagination && options.pagination.querySize >= DEFAULT_MAX_TABLE_QUERY_SIZE) { throw new Error(`No query size above ${DEFAULT_MAX_TABLE_QUERY_SIZE}`); } return buildQuery(options); }, parse: async ( - options: HostUncommonProcessesRequestOptions, + options: HostsUncommonProcessesRequestOptions, response: IEsSearchResponse - ): Promise => { + ): Promise => { const { activePage, cursorStart, fakePossibleCount, querySize } = options.pagination; const totalCount = getOr(0, 'aggregations.process_count.value', response.rawResponse); const buckets = getOr([], 'aggregations.group_by_process.buckets', response.rawResponse); diff --git a/x-pack/test/api_integration/apis/security_solution/index.js b/x-pack/test/api_integration/apis/security_solution/index.js index e4204ae295653c..16a38c0fafbca9 100644 --- a/x-pack/test/api_integration/apis/security_solution/index.js +++ b/x-pack/test/api_integration/apis/security_solution/index.js @@ -20,7 +20,7 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./overview_network')); loadTestFile(require.resolve('./timeline')); loadTestFile(require.resolve('./timeline_details')); - loadTestFile(require.resolve('./uncommon_processes')); + // loadTestFile(require.resolve('./uncommon_processes')); loadTestFile(require.resolve('./users')); // loadTestFile(require.resolve('./tls')); loadTestFile(require.resolve('./feature_controls')); diff --git a/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts b/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts index f1e064bcc37bb3..1ed9a03ecf87ea 100644 --- a/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts +++ b/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts @@ -6,7 +6,9 @@ import expect from '@kbn/expect'; +// @ts-expect-error import { uncommonProcessesQuery } from '../../../../plugins/security_solution/public/hosts/containers/uncommon_processes/index.gql_query'; +// @ts-expect-error import { GetUncommonProcessesQuery } from '../../../../plugins/security_solution/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; From c07a512e4647a40d8e891eb24f5912783b561fba Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Wed, 23 Sep 2020 11:48:54 -0400 Subject: [PATCH 02/35] [Monitoring] Design/UI improvements (#76946) * UI tweaks * Add more page titles * Respect pagination settings * Update snapshot * Fix loc issues * Update node listing * Fix tests * Update icon * Update jobs label * More label changes * Fix tests * Fix tests * PR feedback * Improve responsive design here * PR feedback * Fix tests * Fix test and i18n * Remove unused translations * Fix tests * Tweaks --- .../monitoring/public/angular/app_modules.ts | 6 - .../components/apm/instance/instance.js | 14 +- .../components/apm/instances/instances.js | 8 +- .../public/components/apm/overview/index.js | 14 +- .../public/components/beats/beat/beat.js | 3 + .../components/beats/listing/listing.js | 7 +- .../__snapshots__/overview.test.js.snap | 236 +++++++++--------- .../components/beats/overview/overview.js | 75 +++--- .../components/cluster/overview/apm_panel.js | 16 +- .../cluster/overview/beats_panel.js | 4 +- .../cluster/overview/elasticsearch_panel.js | 38 +-- .../components/cluster/overview/helpers.js | 30 ++- .../cluster/overview/kibana_panel.js | 8 +- .../cluster/overview/logstash_panel.js | 12 +- .../elasticsearch/indices/indices.js | 2 +- .../__snapshots__/cells.test.js.snap | 104 +++++--- .../nodes/__tests__/cells.test.js | 2 + .../components/elasticsearch/nodes/cells.js | 125 +++++++--- .../components/elasticsearch/nodes/nodes.js | 19 +- .../shard_allocation/components/table_head.js | 8 +- .../components/setup_mode/enter_button.scss | 5 +- .../public/directives/beats/beat/index.js | 36 --- .../public/directives/beats/overview/index.js | 30 --- .../public/directives/main/index.html | 53 ++-- .../public/directives/main/index.js | 10 +- .../public/directives/main/index.scss | 3 + .../monitoring/public/services/breadcrumbs.js | 6 +- .../public/views/apm/instance/index.js | 13 +- .../public/views/apm/instances/index.js | 5 +- .../public/views/apm/overview/index.js | 8 +- .../public/views/base_controller.js | 3 + .../public/views/base_eui_table_controller.js | 2 + .../public/views/beats/beat/index.html | 3 +- .../public/views/beats/beat/index.js | 23 +- .../public/views/beats/listing/index.js | 3 + .../public/views/beats/overview/index.html | 2 +- .../public/views/beats/overview/index.js | 17 +- .../public/views/cluster/listing/index.js | 4 + .../public/views/cluster/overview/index.js | 3 + .../public/views/elasticsearch/ccr/index.js | 3 + .../views/elasticsearch/ccr/shard/index.js | 9 + .../public/views/elasticsearch/index/index.js | 6 + .../views/elasticsearch/indices/index.js | 3 + .../views/elasticsearch/ml_jobs/index.js | 3 + .../elasticsearch/node/advanced/index.js | 12 +- .../public/views/elasticsearch/node/index.js | 26 +- .../public/views/elasticsearch/nodes/index.js | 3 + .../elasticsearch/overview/controller.js | 4 + .../public/views/kibana/instance/index.js | 9 + .../public/views/kibana/instances/index.js | 8 +- .../public/views/kibana/overview/index.js | 4 + .../views/logstash/node/advanced/index.js | 9 + .../public/views/logstash/node/index.js | 9 + .../views/logstash/node/pipelines/index.js | 9 + .../public/views/logstash/nodes/index.js | 8 +- .../public/views/logstash/overview/index.js | 4 + .../public/views/logstash/pipeline/index.js | 8 + .../public/views/logstash/pipelines/index.js | 7 +- .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - .../apps/monitoring/cluster/overview.js | 10 +- .../apps/monitoring/elasticsearch/nodes.js | 91 +++++-- .../monitoring/elasticsearch_nodes.js | 43 +++- 63 files changed, 792 insertions(+), 462 deletions(-) delete mode 100644 x-pack/plugins/monitoring/public/directives/beats/beat/index.js delete mode 100644 x-pack/plugins/monitoring/public/directives/beats/overview/index.js create mode 100644 x-pack/plugins/monitoring/public/directives/main/index.scss diff --git a/x-pack/plugins/monitoring/public/angular/app_modules.ts b/x-pack/plugins/monitoring/public/angular/app_modules.ts index 499610045d7717..4ef905fd35fc4a 100644 --- a/x-pack/plugins/monitoring/public/angular/app_modules.ts +++ b/x-pack/plugins/monitoring/public/angular/app_modules.ts @@ -41,10 +41,6 @@ import { licenseProvider } from '../services/license'; // @ts-ignore import { titleProvider } from '../services/title'; // @ts-ignore -import { monitoringBeatsBeatProvider } from '../directives/beats/beat'; -// @ts-ignore -import { monitoringBeatsOverviewProvider } from '../directives/beats/overview'; -// @ts-ignore import { monitoringMlListingProvider } from '../directives/elasticsearch/ml_job_listing'; // @ts-ignore import { monitoringMainProvider } from '../directives/main'; @@ -153,8 +149,6 @@ function createMonitoringAppServices() { function createMonitoringAppDirectives() { angular .module('monitoring/directives', []) - .directive('monitoringBeatsBeat', monitoringBeatsBeatProvider) - .directive('monitoringBeatsOverview', monitoringBeatsOverviewProvider) .directive('monitoringMlListing', monitoringMlListingProvider) .directive('monitoringMain', monitoringMainProvider); } diff --git a/x-pack/plugins/monitoring/public/components/apm/instance/instance.js b/x-pack/plugins/monitoring/public/components/apm/instance/instance.js index 396d2258edd0c8..eec24e741ac416 100644 --- a/x-pack/plugins/monitoring/public/components/apm/instance/instance.js +++ b/x-pack/plugins/monitoring/public/components/apm/instance/instance.js @@ -42,9 +42,7 @@ export function ApmServerInstance({ summary, metrics, ...props }) { const charts = seriesToShow.map((data, index) => ( - - - + )); @@ -55,15 +53,15 @@ export function ApmServerInstance({ summary, metrics, ...props }) {

+ + + + - - - - {charts} diff --git a/x-pack/plugins/monitoring/public/components/apm/instances/instances.js b/x-pack/plugins/monitoring/public/components/apm/instances/instances.js index 6dcfa6dd043aaf..e05ba1878caed7 100644 --- a/x-pack/plugins/monitoring/public/components/apm/instances/instances.js +++ b/x-pack/plugins/monitoring/public/components/apm/instances/instances.js @@ -156,11 +156,11 @@ export function ApmServerInstances({ apms, setupMode }) { /> + + + + - - - - {setupModeCallout} ( - - - + )); @@ -51,15 +49,15 @@ export function ApmOverview({ stats, metrics, ...props }) {

+ + + + - - - - {charts} diff --git a/x-pack/plugins/monitoring/public/components/beats/beat/beat.js b/x-pack/plugins/monitoring/public/components/beats/beat/beat.js index 3fe211c0f2edc4..f489271659bfe1 100644 --- a/x-pack/plugins/monitoring/public/components/beats/beat/beat.js +++ b/x-pack/plugins/monitoring/public/components/beats/beat/beat.js @@ -135,6 +135,9 @@ export function Beat({ summary, metrics, ...props }) { + + + diff --git a/x-pack/plugins/monitoring/public/components/beats/listing/listing.js b/x-pack/plugins/monitoring/public/components/beats/listing/listing.js index be8595e8e6bbe5..60a35e00a4c636 100644 --- a/x-pack/plugins/monitoring/public/components/beats/listing/listing.js +++ b/x-pack/plugins/monitoring/public/components/beats/listing/listing.js @@ -13,6 +13,7 @@ import { EuiSpacer, EuiLink, EuiScreenReaderOnly, + EuiPanel, } from '@elastic/eui'; import { Stats } from '../../beats'; import { formatMetric } from '../../../lib/format_number'; @@ -153,9 +154,11 @@ export class Listing extends PureComponent { /> - + - + + + {setupModeCallOut} - + + + + - - -

- -

-
- - -
+ +

+ +

+
+ +
- - -

- -

-
- - -
+ +

+ +

+
+ +
- - -

- -

-
- - -
+ +

+ +

+
+ +
- +
+ + @@ -212,18 +213,25 @@ exports[`Overview that overview page shows a message if there is no beats data 1 /> - + + + + - + + + diff --git a/x-pack/plugins/monitoring/public/components/beats/overview/overview.js b/x-pack/plugins/monitoring/public/components/beats/overview/overview.js index 83f92ea1b481cd..897f017f44f418 100644 --- a/x-pack/plugins/monitoring/public/components/beats/overview/overview.js +++ b/x-pack/plugins/monitoring/public/components/beats/overview/overview.js @@ -30,46 +30,40 @@ function renderLatestActive(latestActive, latestTypes, latestVersions) { return ( - - -

- -

-
- - -
+ +

+ +

+
+ +
- - -

- -

-
- - -
+ +

+ +

+
+ +
- - -

- -

-
- - -
+ +

+ +

+
+ +
); @@ -118,10 +112,13 @@ export function BeatsOverview({ /> - + - {renderLatestActive(latestActive, latestTypes, latestVersions)} - + + + {renderLatestActive(latestActive, latestTypes, latestVersions)} + + {charts}
diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js index ccbf0b0ec711dd..4bf07710393ea7 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js @@ -55,7 +55,7 @@ export function ApmPanel(props) { {...props} url="apm" title={i18n.translate('xpack.monitoring.cluster.overview.apmPanel.apmTitle', { - defaultMessage: 'APM', + defaultMessage: 'APM server', })} > @@ -70,21 +70,21 @@ export function ApmPanel(props) { aria-label={i18n.translate( 'xpack.monitoring.cluster.overview.apmPanel.overviewLinkAriaLabel', { - defaultMessage: 'APM Overview', + defaultMessage: 'APM server overview', } )} data-test-subj="apmOverview" > - + {formatMetric(props.totalEvents, '0.[0]a')} - + {apmsTotal} }} /> @@ -144,7 +144,7 @@ export function ApmPanel(props) { - + - + {formatMetric(props.totalEvents, '0.[0]a')} - + {props.logs.types.map((log, index) => ( - + - + @@ -276,7 +277,7 @@ export function ElasticsearchPanel(props) { - + - + - + - + {showMlJobs()} - + - + - + - + - + {formatNumber(get(indices, 'docs.count'), 'int_commas')} - + - + - + - + diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js index 6fa533302db48d..7df0a3ca7138e6 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js @@ -111,7 +111,7 @@ export function KibanaPanel(props) { data-test-subj="kibana_overview" data-overview-status={props.status} > - + {props.requests_total} - + - + {formatNumber(props.concurrent_connections, 'int_commas')} - + - + {formatNumber(props.events_in_total, '0.[0]a')} - + - + {props.max_uptime ? formatNumber(props.max_uptime, 'time_since') : 0} - + - + {queueTypes[LOGSTASH.QUEUE_TYPES.MEMORY] || 0} - + } checked={showSystemIndices} diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap index c7081dc439085c..b0b5ceb46d16cb 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap @@ -10,30 +10,46 @@ exports[`Node Listing Metric Cell should format N/A as the metric for an offline exports[`Node Listing Metric Cell should format a non-percentage metric 1`] = `
+
- - 206.3 GB  - - -
- 206.5 GB max -
- 206.3 GB min +
+
+
+
+
+
+
+
+
+ 206.3 GB +
+
@@ -41,30 +57,46 @@ exports[`Node Listing Metric Cell should format a non-percentage metric 1`] = ` exports[`Node Listing Metric Cell should format a percentage metric 1`] = `
+
- - 0%  - - -
- 2% max -
- 0% min +
+
+
+
+
+
+
+
+
+ 0% +
+
diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js index 0c4b4b2b3c3f41..f0b131b65433cf 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js @@ -27,6 +27,7 @@ describe('Node Listing Metric Cell', () => { }, summary: { minVal: 0, maxVal: 2, lastVal: 0, slope: -1 }, }, + 'data-test-subj': 'testCell', }; expect(renderWithIntl()).toMatchSnapshot(); }); @@ -54,6 +55,7 @@ describe('Node Listing Metric Cell', () => { slope: -1, }, }, + 'data-test-subj': 'testCell2', }; expect(renderWithIntl()).toMatchSnapshot(); }); diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.js index 4c3b642213d998..9956dd4da7d8a4 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.js @@ -4,19 +4,42 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useState } from 'react'; import { get } from 'lodash'; import { formatMetric } from '../../../lib/format_number'; -import { EuiText, EuiTitle, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { + EuiText, + EuiPopover, + EuiIcon, + EuiDescriptionList, + EuiSpacer, + EuiKeyboardAccessible, + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +const TRENDING_DOWN = i18n.translate('xpack.monitoring.elasticsearch.node.cells.trendingDownText', { + defaultMessage: 'down', +}); +const TRENDING_UP = i18n.translate('xpack.monitoring.elasticsearch.node.cells.trendingUpText', { + defaultMessage: 'up', +}); + function OfflineCell() { return
N/A
; } -const getSlopeArrow = (slope) => { +const getDirection = (slope) => { + if (slope || slope === 0) { + return slope > 0 ? TRENDING_UP : TRENDING_DOWN; + } + return null; +}; + +const getIcon = (slope) => { if (slope || slope === 0) { - return slope > 0 ? 'up' : 'down'; + return slope > 0 ? 'arrowUp' : 'arrowDown'; } return null; }; @@ -28,40 +51,82 @@ const metricVal = (metric, format, isPercent, units) => { return formatMetric(metric, format, units); }; -const noWrapStyle = { overflowX: 'hidden', whiteSpace: 'nowrap' }; - function MetricCell({ isOnline, metric = {}, isPercent, ...props }) { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const onButtonClick = () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen); + const closePopover = () => setIsPopoverOpen(false); + if (isOnline) { const { lastVal, maxVal, minVal, slope } = get(metric, 'summary', {}); const format = get(metric, 'metric.format'); const units = get(metric, 'metric.units'); + const tooltipItems = [ + { + title: i18n.translate('xpack.monitoring.elasticsearch.node.cells.tooltip.trending', { + defaultMessage: 'Trending', + }), + description: getDirection(slope), + }, + { + title: i18n.translate('xpack.monitoring.elasticsearch.node.cells.tooltip.max', { + defaultMessage: 'Max value', + }), + description: metricVal(maxVal, format, isPercent, units), + }, + { + title: i18n.translate('xpack.monitoring.elasticsearch.node.cells.tooltip.min', { + defaultMessage: 'Min value', + }), + description: metricVal(minVal, format, isPercent, units), + }, + ]; + + const button = ( + + + + ); + return ( - + + - - - {metricVal(lastVal, format, isPercent)} -   - - - - - {i18n.translate('xpack.monitoring.elasticsearch.nodes.cells.maxText', { - defaultMessage: '{metric} max', - values: { - metric: metricVal(maxVal, format, isPercent, units), - }, - })} - - - {i18n.translate('xpack.monitoring.elasticsearch.nodes.cells.minText', { - defaultMessage: '{metric} min', - values: { - metric: metricVal(minVal, format, isPercent, units), - }, - })} - + + + +
+ + + + {i18n.translate('xpack.monitoring.elasticsearch.node.cells.tooltip.preface', { + defaultMessage: 'Applies to current time period', + })} + +
+
+
+ + {metricVal(lastVal, format, isPercent)} + +
); diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js index 43512f8e528f6a..f088f7c0d348af 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js @@ -73,7 +73,6 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler name: i18n.translate('xpack.monitoring.elasticsearch.nodes.nameColumnTitle', { defaultMessage: 'Name', }), - width: '20%', field: 'name', sortable: true, render: (value, node) => { @@ -131,7 +130,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler defaultMessage: 'Alerts', }), field: 'alerts', - width: '175px', + // width: '175px', sortable: true, render: (_field, node) => { return ( @@ -148,6 +147,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler name: i18n.translate('xpack.monitoring.elasticsearch.nodes.statusColumnTitle', { defaultMessage: 'Status', }), + dataType: 'boolean', field: 'isOnline', sortable: true, render: (value) => { @@ -181,22 +181,18 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler name: i18n.translate('xpack.monitoring.elasticsearch.nodes.shardsColumnTitle', { defaultMessage: 'Shards', }), + dataType: 'number', field: 'shardCount', sortable: true, render: (value, node) => { - return node.isOnline ? ( -
- {value} -
- ) : ( - - ); + return node.isOnline ? {value} : ; }, }); if (showCgroupMetricsElasticsearch) { cols.push({ name: cpuUsageColumnTitle, + dataType: 'number', field: 'node_cgroup_quota', sortable: getSortHandler('node_cgroup_quota'), render: (value, node) => ( @@ -213,6 +209,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler name: i18n.translate('xpack.monitoring.elasticsearch.nodes.cpuThrottlingColumnTitle', { defaultMessage: 'CPU Throttling', }), + dataType: 'number', field: 'node_cgroup_throttled', sortable: getSortHandler('node_cgroup_throttled'), render: (value, node) => ( @@ -227,6 +224,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler } else { cols.push({ name: cpuUsageColumnTitle, + dataType: 'number', field: 'node_cpu_utilization', sortable: getSortHandler('node_cpu_utilization'), render: (value, node) => { @@ -245,6 +243,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler name: i18n.translate('xpack.monitoring.elasticsearch.nodes.loadAverageColumnTitle', { defaultMessage: 'Load Average', }), + dataType: 'number', field: 'node_load_average', sortable: getSortHandler('node_load_average'), render: (value, node) => ( @@ -265,6 +264,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler javaVirtualMachine: 'JVM', }, }), + dataType: 'number', field: 'node_jvm_mem_percent', sortable: getSortHandler('node_jvm_mem_percent'), render: (value, node) => ( @@ -281,6 +281,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler name: i18n.translate('xpack.monitoring.elasticsearch.nodes.diskFreeSpaceColumnTitle', { defaultMessage: 'Disk Free Space', }), + dataType: 'number', field: 'node_free_space', sortable: getSortHandler('node_free_space'), render: (value, node) => ( diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js index fd5f28ea02039e..3c875667fe04cf 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js @@ -5,6 +5,7 @@ */ import React from 'react'; +import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSwitch } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -37,7 +38,12 @@ class IndexLabel extends React.Component { $el && $el[0] && unmountComponentAtNode($el[0])); - - scope.$watch('data', (data = {}) => { - render( - , - $el[0] - ); - }); - }, - }; -} diff --git a/x-pack/plugins/monitoring/public/directives/beats/overview/index.js b/x-pack/plugins/monitoring/public/directives/beats/overview/index.js deleted file mode 100644 index 4faf69e13d02c4..00000000000000 --- a/x-pack/plugins/monitoring/public/directives/beats/overview/index.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { BeatsOverview } from '../../../components/beats/overview'; - -export function monitoringBeatsOverviewProvider() { - return { - restrict: 'E', - scope: { - data: '=', - onBrush: '<', - zoomInfo: '<', - }, - link(scope, $el) { - scope.$on('$destroy', () => $el && $el[0] && unmountComponentAtNode($el[0])); - - scope.$watch('data', (data = {}) => { - render( - , - $el[0] - ); - }); - }, - }; -} diff --git a/x-pack/plugins/monitoring/public/directives/main/index.html b/x-pack/plugins/monitoring/public/directives/main/index.html index fabd207d72b1fc..fb24d9e678d565 100644 --- a/x-pack/plugins/monitoring/public/directives/main/index.html +++ b/x-pack/plugins/monitoring/public/directives/main/index.html @@ -1,19 +1,32 @@
-
- - +
+
+
+
+
+
+

{{pageTitle || monitoringMain.instance}}

+
+
+
+
+
+ + +
+
From 14921a037e7e9a2cc607873ae583b4c7303a4e56 Mon Sep 17 00:00:00 2001 From: Phillip Burch Date: Wed, 23 Sep 2020 13:27:28 -0500 Subject: [PATCH 12/35] [Metrics UI] Anomaly Detection setup flow for Metrics (#76787) * adds metrics ml integration * Add ability to create ml jobs from inventory * Fix i18n stuff * Fix typecheck * renames jobs, updates datafeeds * adds allow_no_indices: true for datafeeds * Revert "[Metrics UI] Replace Snapshot API with Metrics API (#76253)" This reverts commit 0ca647286a5ccabfb76203b8cbcb1d13b05f105d. * Add ability to fetch anomalies * Fix typecheck * Fix typecheck * Fix i18n * Fix lint, use the right partition field * Delete log files * Fix merge * Fix merge issues * Update name of jobs * Remove CPU job * [Metrics UI] Replace Snapshot API with Metrics API (#76253) - Remove server/lib/snapshot - Replace backend for /api/infra/snapshot with data from Metrics API - Fixing tests with updates to the snapshot node Co-authored-by: Elastic Machine * Add links back to ML for anomalies and manage jobs * Fix typecheck * Remove unecessary validation Co-authored-by: Michael Hirsch Co-authored-by: Elastic Machine Co-authored-by: Chris Cowan --- .../infra/common/http_api/infra_ml/index.ts | 7 + .../http_api/infra_ml/results/common.ts | 59 ++++ .../common/http_api/infra_ml/results/index.ts | 9 + .../results/metrics_hosts_anomalies.ts | 79 +++++ .../infra_ml/results/metrics_k8s_anomalies.ts | 79 +++++ .../infra/common/infra_ml/anomaly_results.ts | 57 +++ x-pack/plugins/infra/common/infra_ml/index.ts | 11 + .../plugins/infra/common/infra_ml/infra_ml.ts | 52 +++ .../infra/common/infra_ml/job_parameters.ts | 93 +++++ .../infra/common/infra_ml/metrics_hosts_ml.ts | 21 ++ .../infra/common/infra_ml/metrics_k8s_ml.ts | 21 ++ .../public/containers/ml/api/ml_api_types.ts | 28 ++ .../public/containers/ml/api/ml_cleanup.ts | 95 +++++ .../ml/api/ml_get_jobs_summary_api.ts | 93 +++++ .../public/containers/ml/api/ml_get_module.ts | 41 +++ .../containers/ml/api/ml_setup_module_api.ts | 115 ++++++ .../containers/ml/infra_ml_capabilities.tsx | 97 +++++ .../public/containers/ml/infra_ml_cleanup.tsx | 55 +++ .../public/containers/ml/infra_ml_module.tsx | 147 ++++++++ .../ml/infra_ml_module_configuration.ts | 52 +++ .../ml/infra_ml_module_definition.tsx | 76 ++++ .../containers/ml/infra_ml_module_status.tsx | 268 ++++++++++++++ .../containers/ml/infra_ml_module_types.ts | 93 +++++ .../containers/ml/infra_ml_setup_state.ts | 289 +++++++++++++++ .../ml/modules/metrics_hosts/module.tsx | 80 +++++ .../metrics_hosts/module_descriptor.ts | 126 +++++++ .../ml/modules/metrics_k8s/module.tsx | 80 +++++ .../modules/metrics_k8s/module_descriptor.ts | 129 +++++++ .../infra/public/pages/metrics/index.tsx | 210 +++++------ .../anomoly_detection_flyout.tsx | 92 +++++ .../ml/anomaly_detection/flyout_home.tsx | 333 ++++++++++++++++++ .../ml/anomaly_detection/job_setup_screen.tsx | 277 +++++++++++++++ .../subscription_splash_content.tsx | 172 +++++++++ .../hooks/use_metrics_hosts_anomalies.ts | 318 +++++++++++++++++ .../hooks/use_metrics_k8s_anomalies.ts | 322 +++++++++++++++++ x-pack/plugins/infra/server/infra_server.ts | 4 + .../infra/server/lib/infra_ml/common.ts | 89 +++++ .../infra/server/lib/infra_ml/errors.ts | 49 +++ .../infra/server/lib/infra_ml/index.ts | 9 + .../lib/infra_ml/metrics_hosts_anomalies.ts | 289 +++++++++++++++ .../lib/infra_ml/metrics_k8s_anomalies.ts | 272 ++++++++++++++ .../server/lib/infra_ml/queries/common.ts | 68 ++++ .../server/lib/infra_ml/queries/index.ts | 7 + .../infra_ml/queries/log_entry_data_sets.ts | 84 +++++ .../queries/metrics_hosts_anomalies.ts | 131 +++++++ .../infra_ml/queries/metrics_k8s_anomalies.ts | 128 +++++++ .../server/lib/infra_ml/queries/ml_jobs.ts | 24 ++ .../infra/server/routes/infra_ml/index.ts | 7 + .../server/routes/infra_ml/results/index.ts | 8 + .../results/metrics_hosts_anomalies.ts | 125 +++++++ .../infra_ml/results/metrics_k8s_anomalies.ts | 122 +++++++ 51 files changed, 5392 insertions(+), 100 deletions(-) create mode 100644 x-pack/plugins/infra/common/http_api/infra_ml/index.ts create mode 100644 x-pack/plugins/infra/common/http_api/infra_ml/results/common.ts create mode 100644 x-pack/plugins/infra/common/http_api/infra_ml/results/index.ts create mode 100644 x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_hosts_anomalies.ts create mode 100644 x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_k8s_anomalies.ts create mode 100644 x-pack/plugins/infra/common/infra_ml/anomaly_results.ts create mode 100644 x-pack/plugins/infra/common/infra_ml/index.ts create mode 100644 x-pack/plugins/infra/common/infra_ml/infra_ml.ts create mode 100644 x-pack/plugins/infra/common/infra_ml/job_parameters.ts create mode 100644 x-pack/plugins/infra/common/infra_ml/metrics_hosts_ml.ts create mode 100644 x-pack/plugins/infra/common/infra_ml/metrics_k8s_ml.ts create mode 100644 x-pack/plugins/infra/public/containers/ml/api/ml_api_types.ts create mode 100644 x-pack/plugins/infra/public/containers/ml/api/ml_cleanup.ts create mode 100644 x-pack/plugins/infra/public/containers/ml/api/ml_get_jobs_summary_api.ts create mode 100644 x-pack/plugins/infra/public/containers/ml/api/ml_get_module.ts create mode 100644 x-pack/plugins/infra/public/containers/ml/api/ml_setup_module_api.ts create mode 100644 x-pack/plugins/infra/public/containers/ml/infra_ml_capabilities.tsx create mode 100644 x-pack/plugins/infra/public/containers/ml/infra_ml_cleanup.tsx create mode 100644 x-pack/plugins/infra/public/containers/ml/infra_ml_module.tsx create mode 100644 x-pack/plugins/infra/public/containers/ml/infra_ml_module_configuration.ts create mode 100644 x-pack/plugins/infra/public/containers/ml/infra_ml_module_definition.tsx create mode 100644 x-pack/plugins/infra/public/containers/ml/infra_ml_module_status.tsx create mode 100644 x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts create mode 100644 x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts create mode 100644 x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module.tsx create mode 100644 x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts create mode 100644 x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module.tsx create mode 100644 x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts create mode 100644 x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx create mode 100644 x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx create mode 100644 x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx create mode 100644 x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx create mode 100644 x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_hosts_anomalies.ts create mode 100644 x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_k8s_anomalies.ts create mode 100644 x-pack/plugins/infra/server/lib/infra_ml/common.ts create mode 100644 x-pack/plugins/infra/server/lib/infra_ml/errors.ts create mode 100644 x-pack/plugins/infra/server/lib/infra_ml/index.ts create mode 100644 x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts create mode 100644 x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts create mode 100644 x-pack/plugins/infra/server/lib/infra_ml/queries/common.ts create mode 100644 x-pack/plugins/infra/server/lib/infra_ml/queries/index.ts create mode 100644 x-pack/plugins/infra/server/lib/infra_ml/queries/log_entry_data_sets.ts create mode 100644 x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_hosts_anomalies.ts create mode 100644 x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.ts create mode 100644 x-pack/plugins/infra/server/lib/infra_ml/queries/ml_jobs.ts create mode 100644 x-pack/plugins/infra/server/routes/infra_ml/index.ts create mode 100644 x-pack/plugins/infra/server/routes/infra_ml/results/index.ts create mode 100644 x-pack/plugins/infra/server/routes/infra_ml/results/metrics_hosts_anomalies.ts create mode 100644 x-pack/plugins/infra/server/routes/infra_ml/results/metrics_k8s_anomalies.ts diff --git a/x-pack/plugins/infra/common/http_api/infra_ml/index.ts b/x-pack/plugins/infra/common/http_api/infra_ml/index.ts new file mode 100644 index 00000000000000..38684cb22e237f --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/infra_ml/index.ts @@ -0,0 +1,7 @@ +/* + * 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 * from './results'; diff --git a/x-pack/plugins/infra/common/http_api/infra_ml/results/common.ts b/x-pack/plugins/infra/common/http_api/infra_ml/results/common.ts new file mode 100644 index 00000000000000..0474fbd1cfc2fe --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/infra_ml/results/common.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; + +// [Sort field value, tiebreaker value] +export const paginationCursorRT = rt.tuple([ + rt.union([rt.string, rt.number]), + rt.union([rt.string, rt.number]), +]); + +export type PaginationCursor = rt.TypeOf; + +export const anomalyTypeRT = rt.keyof({ + metrics_hosts: null, + metrics_k8s: null, +}); + +export type AnomalyType = rt.TypeOf; + +const sortOptionsRT = rt.keyof({ + anomalyScore: null, + dataset: null, + startTime: null, +}); + +const sortDirectionsRT = rt.keyof({ + asc: null, + desc: null, +}); + +const paginationPreviousPageCursorRT = rt.type({ + searchBefore: paginationCursorRT, +}); + +const paginationNextPageCursorRT = rt.type({ + searchAfter: paginationCursorRT, +}); + +export const paginationRT = rt.intersection([ + rt.type({ + pageSize: rt.number, + }), + rt.partial({ + cursor: rt.union([paginationPreviousPageCursorRT, paginationNextPageCursorRT]), + }), +]); + +export type Pagination = rt.TypeOf; + +export const sortRT = rt.type({ + field: sortOptionsRT, + direction: sortDirectionsRT, +}); + +export type Sort = rt.TypeOf; diff --git a/x-pack/plugins/infra/common/http_api/infra_ml/results/index.ts b/x-pack/plugins/infra/common/http_api/infra_ml/results/index.ts new file mode 100644 index 00000000000000..efd597a043e072 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/infra_ml/results/index.ts @@ -0,0 +1,9 @@ +/* + * 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 * from './metrics_hosts_anomalies'; +export * from './metrics_k8s_anomalies'; +export * from './common'; diff --git a/x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_hosts_anomalies.ts b/x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_hosts_anomalies.ts new file mode 100644 index 00000000000000..9fdac09fec20e2 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_hosts_anomalies.ts @@ -0,0 +1,79 @@ +/* + * 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 * as rt from 'io-ts'; + +import { timeRangeRT, routeTimingMetadataRT } from '../../shared'; +import { anomalyTypeRT, paginationCursorRT, sortRT, paginationRT } from './common'; + +export const INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH = + '/api/infra/infra_ml/results/metrics_hosts_anomalies'; + +const metricsHostAnomalyCommonFieldsRT = rt.type({ + id: rt.string, + anomalyScore: rt.number, + typical: rt.number, + actual: rt.number, + type: anomalyTypeRT, + duration: rt.number, + startTime: rt.number, + jobId: rt.string, +}); +const metricsHostsAnomalyRT = metricsHostAnomalyCommonFieldsRT; + +export type MetricsHostsAnomaly = rt.TypeOf; + +export const getMetricsHostsAnomaliesSuccessReponsePayloadRT = rt.intersection([ + rt.type({ + data: rt.intersection([ + rt.type({ + anomalies: rt.array(metricsHostsAnomalyRT), + // Signifies there are more entries backwards or forwards. If this was a request + // for a previous page, there are more previous pages, if this was a request for a next page, + // there are more next pages. + hasMoreEntries: rt.boolean, + }), + rt.partial({ + paginationCursors: rt.type({ + // The cursor to use to fetch the previous page + previousPageCursor: paginationCursorRT, + // The cursor to use to fetch the next page + nextPageCursor: paginationCursorRT, + }), + }), + ]), + }), + rt.partial({ + timing: routeTimingMetadataRT, + }), +]); + +export type GetMetricsHostsAnomaliesSuccessResponsePayload = rt.TypeOf< + typeof getMetricsHostsAnomaliesSuccessReponsePayloadRT +>; + +export const getMetricsHostsAnomaliesRequestPayloadRT = rt.type({ + data: rt.intersection([ + rt.type({ + // the ID of the source configuration + sourceId: rt.string, + // the time range to fetch the log entry anomalies from + timeRange: timeRangeRT, + }), + rt.partial({ + // Pagination properties + pagination: paginationRT, + // Sort properties + sort: sortRT, + // // Dataset filters + // datasets: rt.array(rt.string), + }), + ]), +}); + +export type GetMetricsHostsAnomaliesRequestPayload = rt.TypeOf< + typeof getMetricsHostsAnomaliesRequestPayloadRT +>; diff --git a/x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_k8s_anomalies.ts b/x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_k8s_anomalies.ts new file mode 100644 index 00000000000000..ab1f245a74c0cb --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_k8s_anomalies.ts @@ -0,0 +1,79 @@ +/* + * 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 * as rt from 'io-ts'; + +import { timeRangeRT, routeTimingMetadataRT } from '../../shared'; +import { paginationCursorRT, anomalyTypeRT, sortRT, paginationRT } from './common'; + +export const INFA_ML_GET_METRICS_K8S_ANOMALIES_PATH = + '/api/infra/infra_ml/results/metrics_k8s_anomalies'; + +const metricsK8sAnomalyCommonFieldsRT = rt.type({ + id: rt.string, + anomalyScore: rt.number, + typical: rt.number, + actual: rt.number, + type: anomalyTypeRT, + duration: rt.number, + startTime: rt.number, + jobId: rt.string, +}); +const metricsK8sAnomalyRT = metricsK8sAnomalyCommonFieldsRT; + +export type MetricsK8sAnomaly = rt.TypeOf; + +export const getMetricsK8sAnomaliesSuccessReponsePayloadRT = rt.intersection([ + rt.type({ + data: rt.intersection([ + rt.type({ + anomalies: rt.array(metricsK8sAnomalyRT), + // Signifies there are more entries backwards or forwards. If this was a request + // for a previous page, there are more previous pages, if this was a request for a next page, + // there are more next pages. + hasMoreEntries: rt.boolean, + }), + rt.partial({ + paginationCursors: rt.type({ + // The cursor to use to fetch the previous page + previousPageCursor: paginationCursorRT, + // The cursor to use to fetch the next page + nextPageCursor: paginationCursorRT, + }), + }), + ]), + }), + rt.partial({ + timing: routeTimingMetadataRT, + }), +]); + +export type GetMetricsK8sAnomaliesSuccessResponsePayload = rt.TypeOf< + typeof getMetricsK8sAnomaliesSuccessReponsePayloadRT +>; + +export const getMetricsK8sAnomaliesRequestPayloadRT = rt.type({ + data: rt.intersection([ + rt.type({ + // the ID of the source configuration + sourceId: rt.string, + // the time range to fetch the log entry anomalies from + timeRange: timeRangeRT, + }), + rt.partial({ + // Pagination properties + pagination: paginationRT, + // Sort properties + sort: sortRT, + // Dataset filters + datasets: rt.array(rt.string), + }), + ]), +}); + +export type GetMetricsK8sAnomaliesRequestPayload = rt.TypeOf< + typeof getMetricsK8sAnomaliesRequestPayloadRT +>; diff --git a/x-pack/plugins/infra/common/infra_ml/anomaly_results.ts b/x-pack/plugins/infra/common/infra_ml/anomaly_results.ts new file mode 100644 index 00000000000000..f4497dbba50567 --- /dev/null +++ b/x-pack/plugins/infra/common/infra_ml/anomaly_results.ts @@ -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. + */ + +export const ML_SEVERITY_SCORES = { + warning: 3, + minor: 25, + major: 50, + critical: 75, +}; + +export type MLSeverityScoreCategories = keyof typeof ML_SEVERITY_SCORES; + +export const ML_SEVERITY_COLORS = { + critical: 'rgb(228, 72, 72)', + major: 'rgb(229, 113, 0)', + minor: 'rgb(255, 221, 0)', + warning: 'rgb(125, 180, 226)', +}; + +export const getSeverityCategoryForScore = ( + score: number +): MLSeverityScoreCategories | undefined => { + if (score >= ML_SEVERITY_SCORES.critical) { + return 'critical'; + } else if (score >= ML_SEVERITY_SCORES.major) { + return 'major'; + } else if (score >= ML_SEVERITY_SCORES.minor) { + return 'minor'; + } else if (score >= ML_SEVERITY_SCORES.warning) { + return 'warning'; + } else { + // Category is too low to include + return undefined; + } +}; + +export const formatAnomalyScore = (score: number) => { + return Math.round(score); +}; + +export const formatOneDecimalPlace = (number: number) => { + return Math.round(number * 10) / 10; +}; + +export const getFriendlyNameForPartitionId = (partitionId: string) => { + return partitionId !== '' ? partitionId : 'unknown'; +}; + +export const compareDatasetsByMaximumAnomalyScore = < + Dataset extends { maximumAnomalyScore: number } +>( + firstDataset: Dataset, + secondDataset: Dataset +) => firstDataset.maximumAnomalyScore - secondDataset.maximumAnomalyScore; diff --git a/x-pack/plugins/infra/common/infra_ml/index.ts b/x-pack/plugins/infra/common/infra_ml/index.ts new file mode 100644 index 00000000000000..88fbbd4f250451 --- /dev/null +++ b/x-pack/plugins/infra/common/infra_ml/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './infra_ml'; +export * from './anomaly_results'; +export * from './job_parameters'; +export * from './metrics_hosts_ml'; +export * from './metrics_k8s_ml'; diff --git a/x-pack/plugins/infra/common/infra_ml/infra_ml.ts b/x-pack/plugins/infra/common/infra_ml/infra_ml.ts new file mode 100644 index 00000000000000..680a2a0fef1145 --- /dev/null +++ b/x-pack/plugins/infra/common/infra_ml/infra_ml.ts @@ -0,0 +1,52 @@ +/* + * 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. + */ + +// combines and abstracts job and datafeed status +export type JobStatus = + | 'unknown' + | 'missing' + | 'initializing' + | 'stopped' + | 'started' + | 'finished' + | 'failed'; + +export type SetupStatus = + | { type: 'initializing' } // acquiring job statuses to determine setup status + | { type: 'unknown' } // job status could not be acquired (failed request etc) + | { type: 'required' } // setup required + | { type: 'pending' } // In the process of setting up the module for the first time or retrying, waiting for response + | { type: 'succeeded' } // setup succeeded, notifying user + | { + type: 'failed'; + reasons: string[]; + } // setup failed, notifying user + | { + type: 'skipped'; + newlyCreated?: boolean; + }; // setup is not necessary + +/** + * Maps a job status to the possibility that results have already been produced + * before this state was reached. + */ +export const isJobStatusWithResults = (jobStatus: JobStatus) => + ['started', 'finished', 'stopped', 'failed'].includes(jobStatus); + +export const isHealthyJobStatus = (jobStatus: JobStatus) => + ['started', 'finished'].includes(jobStatus); + +/** + * Maps a setup status to the possibility that results have already been + * produced before this state was reached. + */ +export const isSetupStatusWithResults = (setupStatus: SetupStatus) => + setupStatus.type === 'skipped'; + +const KIBANA_SAMPLE_DATA_INDICES = ['kibana_sample_data_logs*']; + +export const isExampleDataIndex = (indexName: string) => + KIBANA_SAMPLE_DATA_INDICES.includes(indexName); diff --git a/x-pack/plugins/infra/common/infra_ml/job_parameters.ts b/x-pack/plugins/infra/common/infra_ml/job_parameters.ts new file mode 100644 index 00000000000000..8cd1c9ea6e2ba9 --- /dev/null +++ b/x-pack/plugins/infra/common/infra_ml/job_parameters.ts @@ -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 * as rt from 'io-ts'; + +export const bucketSpan = 900000; + +export const categoriesMessageField = 'message'; + +export const partitionField = 'event.dataset'; + +export const getJobIdPrefix = (spaceId: string, sourceId: string) => + `kibana-metrics-ui-${spaceId}-${sourceId}-`; + +export const getJobId = (spaceId: string, sourceId: string, jobType: string) => + `${getJobIdPrefix(spaceId, sourceId)}${jobType}`; + +export const getDatafeedId = (spaceId: string, sourceId: string, jobType: string) => + `datafeed-${getJobId(spaceId, sourceId, jobType)}`; + +export const datasetFilterRT = rt.union([ + rt.strict({ + type: rt.literal('includeAll'), + }), + rt.strict({ + type: rt.literal('includeSome'), + datasets: rt.array(rt.string), + }), +]); + +export type DatasetFilter = rt.TypeOf; + +export const jobSourceConfigurationRT = rt.partial({ + indexPattern: rt.string, + timestampField: rt.string, + bucketSpan: rt.number, + datasetFilter: datasetFilterRT, +}); + +export type JobSourceConfiguration = rt.TypeOf; + +export const jobCustomSettingsRT = rt.partial({ + job_revision: rt.number, + metrics_source_config: jobSourceConfigurationRT, +}); + +export type JobCustomSettings = rt.TypeOf; + +export const combineDatasetFilters = ( + firstFilter: DatasetFilter, + secondFilter: DatasetFilter +): DatasetFilter => { + if (firstFilter.type === 'includeAll' && secondFilter.type === 'includeAll') { + return { + type: 'includeAll', + }; + } + + const includedDatasets = new Set([ + ...(firstFilter.type === 'includeSome' ? firstFilter.datasets : []), + ...(secondFilter.type === 'includeSome' ? secondFilter.datasets : []), + ]); + + return { + type: 'includeSome', + datasets: [...includedDatasets], + }; +}; + +export const filterDatasetFilter = ( + datasetFilter: DatasetFilter, + predicate: (dataset: string) => boolean +): DatasetFilter => { + if (datasetFilter.type === 'includeAll') { + return datasetFilter; + } else { + const newDatasets = datasetFilter.datasets.filter(predicate); + + if (newDatasets.length > 0) { + return { + type: 'includeSome', + datasets: newDatasets, + }; + } else { + return { + type: 'includeAll', + }; + } + } +}; diff --git a/x-pack/plugins/infra/common/infra_ml/metrics_hosts_ml.ts b/x-pack/plugins/infra/common/infra_ml/metrics_hosts_ml.ts new file mode 100644 index 00000000000000..d09b3be78204f9 --- /dev/null +++ b/x-pack/plugins/infra/common/infra_ml/metrics_hosts_ml.ts @@ -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 * as rt from 'io-ts'; + +export const metricsHostsJobTypeRT = rt.keyof({ + hosts_memory_usage: null, + hosts_network_in: null, + hosts_network_out: null, +}); + +export type MetricsHostsJobType = rt.TypeOf; + +export const metricsHostsJobTypes: MetricsHostsJobType[] = [ + 'hosts_memory_usage', + 'hosts_network_in', + 'hosts_network_out', +]; diff --git a/x-pack/plugins/infra/common/infra_ml/metrics_k8s_ml.ts b/x-pack/plugins/infra/common/infra_ml/metrics_k8s_ml.ts new file mode 100644 index 00000000000000..3c27dbb61a14ac --- /dev/null +++ b/x-pack/plugins/infra/common/infra_ml/metrics_k8s_ml.ts @@ -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 * as rt from 'io-ts'; + +export const metricK8sJobTypeRT = rt.keyof({ + k8s_memory_usage: null, + k8s_network_in: null, + k8s_network_out: null, +}); + +export type MetricK8sJobType = rt.TypeOf; + +export const metricsK8SJobTypes: MetricK8sJobType[] = [ + 'k8s_memory_usage', + 'k8s_network_in', + 'k8s_network_out', +]; diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_api_types.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_api_types.ts new file mode 100644 index 00000000000000..ee70edc31d49ba --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_api_types.ts @@ -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 * as rt from 'io-ts'; + +export const getMlCapabilitiesResponsePayloadRT = rt.type({ + capabilities: rt.type({ + canGetJobs: rt.boolean, + canCreateJob: rt.boolean, + canDeleteJob: rt.boolean, + canOpenJob: rt.boolean, + canCloseJob: rt.boolean, + canForecastJob: rt.boolean, + canGetDatafeeds: rt.boolean, + canStartStopDatafeed: rt.boolean, + canUpdateJob: rt.boolean, + canUpdateDatafeed: rt.boolean, + canPreviewDatafeed: rt.boolean, + }), + isPlatinumOrTrialLicense: rt.boolean, + mlFeatureEnabledInSpace: rt.boolean, + upgradeInProgress: rt.boolean, +}); + +export type GetMlCapabilitiesResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_cleanup.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_cleanup.ts new file mode 100644 index 00000000000000..23fa338e74f147 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_cleanup.ts @@ -0,0 +1,95 @@ +/* + * 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 * as rt from 'io-ts'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { npStart } from '../../../legacy_singletons'; + +import { getDatafeedId, getJobId } from '../../../../common/infra_ml'; +import { throwErrors, createPlainError } from '../../../../common/runtime_types'; + +export const callDeleteJobs = async ( + spaceId: string, + sourceId: string, + jobTypes: JobType[] +) => { + // NOTE: Deleting the jobs via this API will delete the datafeeds at the same time + const deleteJobsResponse = await npStart.http.fetch('/api/ml/jobs/delete_jobs', { + method: 'POST', + body: JSON.stringify( + deleteJobsRequestPayloadRT.encode({ + jobIds: jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)), + }) + ), + }); + + return pipe( + deleteJobsResponsePayloadRT.decode(deleteJobsResponse), + fold(throwErrors(createPlainError), identity) + ); +}; + +export const callGetJobDeletionTasks = async () => { + const jobDeletionTasksResponse = await npStart.http.fetch('/api/ml/jobs/deleting_jobs_tasks'); + + return pipe( + getJobDeletionTasksResponsePayloadRT.decode(jobDeletionTasksResponse), + fold(throwErrors(createPlainError), identity) + ); +}; + +export const callStopDatafeeds = async ( + spaceId: string, + sourceId: string, + jobTypes: JobType[] +) => { + // Stop datafeed due to https://github.com/elastic/kibana/issues/44652 + const stopDatafeedResponse = await npStart.http.fetch('/api/ml/jobs/stop_datafeeds', { + method: 'POST', + body: JSON.stringify( + stopDatafeedsRequestPayloadRT.encode({ + datafeedIds: jobTypes.map((jobType) => getDatafeedId(spaceId, sourceId, jobType)), + }) + ), + }); + + return pipe( + stopDatafeedsResponsePayloadRT.decode(stopDatafeedResponse), + fold(throwErrors(createPlainError), identity) + ); +}; + +export const deleteJobsRequestPayloadRT = rt.type({ + jobIds: rt.array(rt.string), +}); + +export type DeleteJobsRequestPayload = rt.TypeOf; + +export const deleteJobsResponsePayloadRT = rt.record( + rt.string, + rt.type({ + deleted: rt.boolean, + }) +); + +export type DeleteJobsResponsePayload = rt.TypeOf; + +export const getJobDeletionTasksResponsePayloadRT = rt.type({ + jobIds: rt.array(rt.string), +}); + +export const stopDatafeedsRequestPayloadRT = rt.type({ + datafeedIds: rt.array(rt.string), +}); + +export const stopDatafeedsResponsePayloadRT = rt.record( + rt.string, + rt.type({ + stopped: rt.boolean, + }) +); diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_get_jobs_summary_api.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_get_jobs_summary_api.ts new file mode 100644 index 00000000000000..3fddb63f69791b --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_get_jobs_summary_api.ts @@ -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 { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { pipe } from 'fp-ts/lib/pipeable'; +import * as rt from 'io-ts'; +import { npStart } from '../../../legacy_singletons'; + +import { getJobId, jobCustomSettingsRT } from '../../../../common/infra_ml'; +import { createPlainError, throwErrors } from '../../../../common/runtime_types'; + +export const callJobsSummaryAPI = async ( + spaceId: string, + sourceId: string, + jobTypes: JobType[] +) => { + const response = await npStart.http.fetch('/api/ml/jobs/jobs_summary', { + method: 'POST', + body: JSON.stringify( + fetchJobStatusRequestPayloadRT.encode({ + jobIds: jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)), + }) + ), + }); + return pipe( + fetchJobStatusResponsePayloadRT.decode(response), + fold(throwErrors(createPlainError), identity) + ); +}; + +export const fetchJobStatusRequestPayloadRT = rt.type({ + jobIds: rt.array(rt.string), +}); + +export type FetchJobStatusRequestPayload = rt.TypeOf; + +const datafeedStateRT = rt.keyof({ + started: null, + stopped: null, + stopping: null, + '': null, +}); + +const jobStateRT = rt.keyof({ + closed: null, + closing: null, + deleting: null, + failed: null, + opened: null, + opening: null, +}); + +const jobCategorizationStatusRT = rt.keyof({ + ok: null, + warn: null, +}); + +const jobModelSizeStatsRT = rt.type({ + categorization_status: jobCategorizationStatusRT, + categorized_doc_count: rt.number, + dead_category_count: rt.number, + frequent_category_count: rt.number, + rare_category_count: rt.number, + total_category_count: rt.number, +}); + +export type JobModelSizeStats = rt.TypeOf; + +export const jobSummaryRT = rt.intersection([ + rt.type({ + id: rt.string, + jobState: jobStateRT, + }), + rt.partial({ + datafeedIndices: rt.array(rt.string), + datafeedState: datafeedStateRT, + fullJob: rt.partial({ + custom_settings: jobCustomSettingsRT, + finished_time: rt.number, + model_size_stats: jobModelSizeStatsRT, + }), + }), +]); + +export type JobSummary = rt.TypeOf; + +export const fetchJobStatusResponsePayloadRT = rt.array(jobSummaryRT); + +export type FetchJobStatusResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_get_module.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_get_module.ts new file mode 100644 index 00000000000000..d492522c120a11 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_get_module.ts @@ -0,0 +1,41 @@ +/* + * 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 { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { pipe } from 'fp-ts/lib/pipeable'; +import * as rt from 'io-ts'; +import { npStart } from '../../../legacy_singletons'; + +import { jobCustomSettingsRT } from '../../../../common/log_analysis'; +import { createPlainError, throwErrors } from '../../../../common/runtime_types'; + +export const callGetMlModuleAPI = async (moduleId: string) => { + const response = await npStart.http.fetch(`/api/ml/modules/get_module/${moduleId}`, { + method: 'GET', + }); + + return pipe( + getMlModuleResponsePayloadRT.decode(response), + fold(throwErrors(createPlainError), identity) + ); +}; + +const jobDefinitionRT = rt.type({ + id: rt.string, + config: rt.type({ + custom_settings: jobCustomSettingsRT, + }), +}); + +export type JobDefinition = rt.TypeOf; + +const getMlModuleResponsePayloadRT = rt.type({ + id: rt.string, + jobs: rt.array(jobDefinitionRT), +}); + +export type GetMlModuleResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_setup_module_api.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_setup_module_api.ts new file mode 100644 index 00000000000000..06b0e075387b01 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_setup_module_api.ts @@ -0,0 +1,115 @@ +/* + * 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 { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { pipe } from 'fp-ts/lib/pipeable'; +import * as rt from 'io-ts'; +import { npStart } from '../../../legacy_singletons'; + +import { getJobIdPrefix, jobCustomSettingsRT } from '../../../../common/infra_ml'; +import { createPlainError, throwErrors } from '../../../../common/runtime_types'; + +export const callSetupMlModuleAPI = async ( + moduleId: string, + start: number | undefined, + end: number | undefined, + spaceId: string, + sourceId: string, + indexPattern: string, + jobOverrides: SetupMlModuleJobOverrides[] = [], + datafeedOverrides: SetupMlModuleDatafeedOverrides[] = [], + query?: object +) => { + const response = await npStart.http.fetch(`/api/ml/modules/setup/${moduleId}`, { + method: 'POST', + body: JSON.stringify( + setupMlModuleRequestPayloadRT.encode({ + start, + end, + indexPatternName: indexPattern, + prefix: getJobIdPrefix(spaceId, sourceId), + startDatafeed: true, + jobOverrides, + datafeedOverrides, + query, + }) + ), + }); + + return pipe( + setupMlModuleResponsePayloadRT.decode(response), + fold(throwErrors(createPlainError), identity) + ); +}; + +const setupMlModuleTimeParamsRT = rt.partial({ + start: rt.number, + end: rt.number, +}); + +const setupMlModuleJobOverridesRT = rt.type({ + job_id: rt.string, + custom_settings: jobCustomSettingsRT, +}); + +export type SetupMlModuleJobOverrides = rt.TypeOf; + +const setupMlModuleDatafeedOverridesRT = rt.object; + +export type SetupMlModuleDatafeedOverrides = rt.TypeOf; + +const setupMlModuleRequestParamsRT = rt.intersection([ + rt.strict({ + indexPatternName: rt.string, + prefix: rt.string, + startDatafeed: rt.boolean, + jobOverrides: rt.array(setupMlModuleJobOverridesRT), + datafeedOverrides: rt.array(setupMlModuleDatafeedOverridesRT), + }), + rt.exact( + rt.partial({ + query: rt.object, + }) + ), +]); + +const setupMlModuleRequestPayloadRT = rt.intersection([ + setupMlModuleTimeParamsRT, + setupMlModuleRequestParamsRT, +]); + +const setupErrorResponseRT = rt.type({ + msg: rt.string, +}); + +const datafeedSetupResponseRT = rt.intersection([ + rt.type({ + id: rt.string, + started: rt.boolean, + success: rt.boolean, + }), + rt.partial({ + error: setupErrorResponseRT, + }), +]); + +const jobSetupResponseRT = rt.intersection([ + rt.type({ + id: rt.string, + success: rt.boolean, + }), + rt.partial({ + error: setupErrorResponseRT, + }), +]); + +const setupMlModuleResponsePayloadRT = rt.type({ + datafeeds: rt.array(datafeedSetupResponseRT), + jobs: rt.array(jobSetupResponseRT), +}); + +export type SetupMlModuleResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_capabilities.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_capabilities.tsx new file mode 100644 index 00000000000000..f4c90a459af6ac --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_capabilities.tsx @@ -0,0 +1,97 @@ +/* + * 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 createContainer from 'constate'; +import { useMemo, useState, useEffect } from 'react'; +import { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { identity } from 'fp-ts/lib/function'; +import { useTrackedPromise } from '../../utils/use_tracked_promise'; +import { npStart } from '../../legacy_singletons'; +import { + getMlCapabilitiesResponsePayloadRT, + GetMlCapabilitiesResponsePayload, +} from './api/ml_api_types'; +import { throwErrors, createPlainError } from '../../../common/runtime_types'; + +export const useInfraMLCapabilities = () => { + const [mlCapabilities, setMlCapabilities] = useState( + initialMlCapabilities + ); + + const [fetchMlCapabilitiesRequest, fetchMlCapabilities] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + const rawResponse = await npStart.http.fetch('/api/ml/ml_capabilities'); + + return pipe( + getMlCapabilitiesResponsePayloadRT.decode(rawResponse), + fold(throwErrors(createPlainError), identity) + ); + }, + onResolve: (response) => { + setMlCapabilities(response); + }, + }, + [] + ); + + useEffect(() => { + fetchMlCapabilities(); + }, [fetchMlCapabilities]); + + const isLoading = useMemo(() => fetchMlCapabilitiesRequest.state === 'pending', [ + fetchMlCapabilitiesRequest.state, + ]); + + const hasInfraMLSetupCapabilities = mlCapabilities.capabilities.canCreateJob; + const hasInfraMLReadCapabilities = mlCapabilities.capabilities.canGetJobs; + const hasInfraMLCapabilites = + mlCapabilities.isPlatinumOrTrialLicense && mlCapabilities.mlFeatureEnabledInSpace; + + return { + hasInfraMLCapabilites, + hasInfraMLReadCapabilities, + hasInfraMLSetupCapabilities, + isLoading, + }; +}; + +export const [InfraMLCapabilitiesProvider, useInfraMLCapabilitiesContext] = createContainer( + useInfraMLCapabilities +); + +const initialMlCapabilities = { + capabilities: { + canGetJobs: false, + canCreateJob: false, + canDeleteJob: false, + canOpenJob: false, + canCloseJob: false, + canForecastJob: false, + canGetDatafeeds: false, + canStartStopDatafeed: false, + canUpdateJob: false, + canUpdateDatafeed: false, + canPreviewDatafeed: false, + canGetCalendars: false, + canCreateCalendar: false, + canDeleteCalendar: false, + canGetFilters: false, + canCreateFilter: false, + canDeleteFilter: false, + canFindFileStructure: false, + canGetDataFrameJobs: false, + canDeleteDataFrameJob: false, + canPreviewDataFrameJob: false, + canCreateDataFrameJob: false, + canStartStopDataFrameJob: false, + }, + isPlatinumOrTrialLicense: false, + mlFeatureEnabledInSpace: false, + upgradeInProgress: false, +}; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_cleanup.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_cleanup.tsx new file mode 100644 index 00000000000000..736982c8043b14 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_cleanup.tsx @@ -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 { getJobId } from '../../../common/infra_ml'; +import { callDeleteJobs, callGetJobDeletionTasks, callStopDatafeeds } from './api/ml_cleanup'; + +export const cleanUpJobsAndDatafeeds = async ( + spaceId: string, + sourceId: string, + jobTypes: JobType[] +) => { + try { + await callStopDatafeeds(spaceId, sourceId, jobTypes); + } catch (err) { + // Proceed only if datafeed has been deleted or didn't exist in the first place + if (err?.res?.status !== 404) { + throw err; + } + } + + return await deleteJobs(spaceId, sourceId, jobTypes); +}; + +const deleteJobs = async ( + spaceId: string, + sourceId: string, + jobTypes: JobType[] +) => { + const deleteJobsResponse = await callDeleteJobs(spaceId, sourceId, jobTypes); + await waitUntilJobsAreDeleted(spaceId, sourceId, jobTypes); + return deleteJobsResponse; +}; + +const waitUntilJobsAreDeleted = async ( + spaceId: string, + sourceId: string, + jobTypes: JobType[] +) => { + const moduleJobIds = jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)); + while (true) { + const { jobIds: jobIdsBeingDeleted } = await callGetJobDeletionTasks(); + const needToWait = jobIdsBeingDeleted.some((jobId) => moduleJobIds.includes(jobId)); + + if (needToWait) { + await timeout(1000); + } else { + return true; + } + } +}; + +const timeout = (ms: number) => new Promise((res) => setTimeout(res, ms)); diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_module.tsx new file mode 100644 index 00000000000000..349541d108f5ed --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module.tsx @@ -0,0 +1,147 @@ +/* + * 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 { useCallback, useMemo } from 'react'; +import { DatasetFilter } from '../../../common/infra_ml'; +import { useTrackedPromise } from '../../utils/use_tracked_promise'; +import { useModuleStatus } from './infra_ml_module_status'; +import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types'; + +export const useInfraMLModule = ({ + sourceConfiguration, + moduleDescriptor, +}: { + sourceConfiguration: ModuleSourceConfiguration; + moduleDescriptor: ModuleDescriptor; +}) => { + const { spaceId, sourceId, timestampField } = sourceConfiguration; + const [moduleStatus, dispatchModuleStatus] = useModuleStatus(moduleDescriptor.jobTypes); + + const [, fetchJobStatus] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + dispatchModuleStatus({ type: 'fetchingJobStatuses' }); + return await moduleDescriptor.getJobSummary(spaceId, sourceId); + }, + onResolve: (jobResponse) => { + dispatchModuleStatus({ + type: 'fetchedJobStatuses', + payload: jobResponse, + spaceId, + sourceId, + }); + }, + onReject: () => { + dispatchModuleStatus({ type: 'failedFetchingJobStatuses' }); + }, + }, + [spaceId, sourceId] + ); + + const [, setUpModule] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async ( + selectedIndices: string[], + start: number | undefined, + end: number | undefined, + datasetFilter: DatasetFilter, + partitionField?: string + ) => { + dispatchModuleStatus({ type: 'startedSetup' }); + const setupResult = await moduleDescriptor.setUpModule( + start, + end, + datasetFilter, + { + indices: selectedIndices, + sourceId, + spaceId, + timestampField, + }, + partitionField + ); + const jobSummaries = await moduleDescriptor.getJobSummary(spaceId, sourceId); + return { setupResult, jobSummaries }; + }, + onResolve: ({ setupResult: { datafeeds, jobs }, jobSummaries }) => { + dispatchModuleStatus({ + type: 'finishedSetup', + datafeedSetupResults: datafeeds, + jobSetupResults: jobs, + jobSummaries, + spaceId, + sourceId, + }); + }, + onReject: () => { + dispatchModuleStatus({ type: 'failedSetup' }); + }, + }, + [moduleDescriptor.setUpModule, spaceId, sourceId, timestampField] + ); + + const [cleanUpModuleRequest, cleanUpModule] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + return await moduleDescriptor.cleanUpModule(spaceId, sourceId); + }, + }, + [spaceId, sourceId] + ); + + const isCleaningUp = useMemo(() => cleanUpModuleRequest.state === 'pending', [ + cleanUpModuleRequest.state, + ]); + + const cleanUpAndSetUpModule = useCallback( + ( + selectedIndices: string[], + start: number | undefined, + end: number | undefined, + datasetFilter: DatasetFilter, + partitionField?: string + ) => { + dispatchModuleStatus({ type: 'startedSetup' }); + cleanUpModule() + .then(() => { + setUpModule(selectedIndices, start, end, datasetFilter, partitionField); + }) + .catch(() => { + dispatchModuleStatus({ type: 'failedSetup' }); + }); + }, + [cleanUpModule, dispatchModuleStatus, setUpModule] + ); + + const viewResults = useCallback(() => { + dispatchModuleStatus({ type: 'viewedResults' }); + }, [dispatchModuleStatus]); + + const jobIds = useMemo(() => moduleDescriptor.getJobIds(spaceId, sourceId), [ + moduleDescriptor, + spaceId, + sourceId, + ]); + + return { + cleanUpAndSetUpModule, + cleanUpModule, + fetchJobStatus, + isCleaningUp, + jobIds, + jobStatus: moduleStatus.jobStatus, + jobSummaries: moduleStatus.jobSummaries, + lastSetupErrorMessages: moduleStatus.lastSetupErrorMessages, + moduleDescriptor, + setUpModule, + setupStatus: moduleStatus.setupStatus, + sourceConfiguration, + viewResults, + }; +}; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_configuration.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_configuration.ts new file mode 100644 index 00000000000000..2d90f5d5310109 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_configuration.ts @@ -0,0 +1,52 @@ +/* + * 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 { useMemo } from 'react'; +import { JobSummary } from './api/ml_get_jobs_summary_api'; +import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types'; + +export const useInfraMLModuleConfiguration = ({ + moduleDescriptor, + sourceConfiguration, +}: { + moduleDescriptor: ModuleDescriptor; + sourceConfiguration: ModuleSourceConfiguration; +}) => { + const getIsJobConfigurationOutdated = useMemo( + () => isJobConfigurationOutdated(moduleDescriptor, sourceConfiguration), + [sourceConfiguration, moduleDescriptor] + ); + + return { + getIsJobConfigurationOutdated, + }; +}; + +export const isJobConfigurationOutdated = ( + { bucketSpan }: ModuleDescriptor, + currentSourceConfiguration: ModuleSourceConfiguration +) => (jobSummary: JobSummary): boolean => { + if (!jobSummary.fullJob || !jobSummary.fullJob.custom_settings) { + return false; + } + + const jobConfiguration = jobSummary.fullJob.custom_settings.metrics_source_config; + + return !( + jobConfiguration && + jobConfiguration.bucketSpan === bucketSpan && + jobConfiguration.indexPattern && + isSubset( + new Set(jobConfiguration.indexPattern.split(',')), + new Set(currentSourceConfiguration.indices) + ) && + jobConfiguration.timestampField === currentSourceConfiguration.timestampField + ); +}; + +const isSubset = (subset: Set, superset: Set) => { + return Array.from(subset).every((subsetElement) => superset.has(subsetElement)); +}; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_definition.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_definition.tsx new file mode 100644 index 00000000000000..3c7ffcfd4a4e25 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_definition.tsx @@ -0,0 +1,76 @@ +/* + * 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 { useCallback, useMemo, useState } from 'react'; +import { getJobId } from '../../../common/log_analysis'; +import { useTrackedPromise } from '../../utils/use_tracked_promise'; +import { JobSummary } from './api/ml_get_jobs_summary_api'; +import { GetMlModuleResponsePayload, JobDefinition } from './api/ml_get_module'; +import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types'; + +export const useInfraMLModuleDefinition = ({ + sourceConfiguration: { spaceId, sourceId }, + moduleDescriptor, +}: { + sourceConfiguration: ModuleSourceConfiguration; + moduleDescriptor: ModuleDescriptor; +}) => { + const [moduleDefinition, setModuleDefinition] = useState< + GetMlModuleResponsePayload | undefined + >(); + + const jobDefinitionByJobId = useMemo( + () => + moduleDefinition + ? moduleDefinition.jobs.reduce>( + (accumulatedJobDefinitions, jobDefinition) => ({ + ...accumulatedJobDefinitions, + [getJobId(spaceId, sourceId, jobDefinition.id)]: jobDefinition, + }), + {} + ) + : {}, + [moduleDefinition, sourceId, spaceId] + ); + + const [fetchModuleDefinitionRequest, fetchModuleDefinition] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + return await moduleDescriptor.getModuleDefinition(); + }, + onResolve: (response) => { + setModuleDefinition(response); + }, + onReject: () => { + setModuleDefinition(undefined); + }, + }, + [moduleDescriptor.getModuleDefinition, spaceId, sourceId] + ); + + const getIsJobDefinitionOutdated = useCallback( + (jobSummary: JobSummary): boolean => { + const jobDefinition: JobDefinition | undefined = jobDefinitionByJobId[jobSummary.id]; + + if (jobDefinition == null) { + return false; + } + + const currentRevision = jobDefinition?.config.custom_settings.job_revision; + return (jobSummary.fullJob?.custom_settings?.job_revision ?? 0) < (currentRevision ?? 0); + }, + [jobDefinitionByJobId] + ); + + return { + fetchModuleDefinition, + fetchModuleDefinitionRequestState: fetchModuleDefinitionRequest.state, + getIsJobDefinitionOutdated, + jobDefinitionByJobId, + moduleDefinition, + }; +}; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_status.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_status.tsx new file mode 100644 index 00000000000000..63d479546b44ff --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_status.tsx @@ -0,0 +1,268 @@ +/* + * 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 { useReducer } from 'react'; + +import { + JobStatus, + getDatafeedId, + getJobId, + isJobStatusWithResults, + SetupStatus, +} from '../../../common/infra_ml'; +import { FetchJobStatusResponsePayload, JobSummary } from './api/ml_get_jobs_summary_api'; +import { SetupMlModuleResponsePayload } from './api/ml_setup_module_api'; +import { MandatoryProperty } from '../../../common/utility_types'; + +interface StatusReducerState { + jobStatus: Record; + jobSummaries: JobSummary[]; + lastSetupErrorMessages: string[]; + setupStatus: SetupStatus; +} + +type StatusReducerAction = + | { type: 'startedSetup' } + | { + type: 'finishedSetup'; + sourceId: string; + spaceId: string; + jobSetupResults: SetupMlModuleResponsePayload['jobs']; + jobSummaries: FetchJobStatusResponsePayload; + datafeedSetupResults: SetupMlModuleResponsePayload['datafeeds']; + } + | { type: 'failedSetup' } + | { type: 'fetchingJobStatuses' } + | { + type: 'fetchedJobStatuses'; + spaceId: string; + sourceId: string; + payload: FetchJobStatusResponsePayload; + } + | { type: 'failedFetchingJobStatuses' } + | { type: 'viewedResults' }; + +const createInitialState = ({ + jobTypes, +}: { + jobTypes: JobType[]; +}): StatusReducerState => ({ + jobStatus: jobTypes.reduce( + (accumulatedJobStatus, jobType) => ({ + ...accumulatedJobStatus, + [jobType]: 'unknown', + }), + {} as Record + ), + jobSummaries: [], + lastSetupErrorMessages: [], + setupStatus: { type: 'initializing' }, +}); + +const createStatusReducer = (jobTypes: JobType[]) => ( + state: StatusReducerState, + action: StatusReducerAction +): StatusReducerState => { + switch (action.type) { + case 'startedSetup': { + return { + ...state, + jobStatus: jobTypes.reduce( + (accumulatedJobStatus, jobType) => ({ + ...accumulatedJobStatus, + [jobType]: 'initializing', + }), + {} as Record + ), + setupStatus: { type: 'pending' }, + }; + } + case 'finishedSetup': { + const { datafeedSetupResults, jobSetupResults, jobSummaries, spaceId, sourceId } = action; + const nextJobStatus = jobTypes.reduce( + (accumulatedJobStatus, jobType) => ({ + ...accumulatedJobStatus, + [jobType]: + hasSuccessfullyCreatedJob(getJobId(spaceId, sourceId, jobType))(jobSetupResults) && + hasSuccessfullyStartedDatafeed(getDatafeedId(spaceId, sourceId, jobType))( + datafeedSetupResults + ) + ? 'started' + : 'failed', + }), + {} as Record + ); + const nextSetupStatus: SetupStatus = Object.values(nextJobStatus).every( + (jobState) => jobState === 'started' + ) + ? { type: 'succeeded' } + : { + type: 'failed', + reasons: [ + ...Object.values(datafeedSetupResults) + .filter(hasError) + .map((datafeed) => datafeed.error.msg), + ...Object.values(jobSetupResults) + .filter(hasError) + .map((job) => job.error.msg), + ], + }; + + return { + ...state, + jobStatus: nextJobStatus, + jobSummaries, + setupStatus: nextSetupStatus, + }; + } + case 'failedSetup': { + return { + ...state, + jobStatus: jobTypes.reduce( + (accumulatedJobStatus, jobType) => ({ + ...accumulatedJobStatus, + [jobType]: 'failed', + }), + {} as Record + ), + setupStatus: { type: 'failed', reasons: ['unknown'] }, + }; + } + case 'fetchingJobStatuses': { + return { + ...state, + setupStatus: + state.setupStatus.type === 'unknown' ? { type: 'initializing' } : state.setupStatus, + }; + } + case 'fetchedJobStatuses': { + const { payload: jobSummaries, spaceId, sourceId } = action; + const { setupStatus } = state; + const nextJobStatus = jobTypes.reduce( + (accumulatedJobStatus, jobType) => ({ + ...accumulatedJobStatus, + [jobType]: getJobStatus(getJobId(spaceId, sourceId, jobType))(jobSummaries), + }), + {} as Record + ); + const nextSetupStatus = getSetupStatus(nextJobStatus)(setupStatus); + + return { + ...state, + jobSummaries, + jobStatus: nextJobStatus, + setupStatus: nextSetupStatus, + }; + } + case 'failedFetchingJobStatuses': { + return { + ...state, + setupStatus: { type: 'unknown' }, + jobStatus: jobTypes.reduce( + (accumulatedJobStatus, jobType) => ({ + ...accumulatedJobStatus, + [jobType]: 'unknown', + }), + {} as Record + ), + }; + } + case 'viewedResults': { + return { + ...state, + setupStatus: { type: 'skipped', newlyCreated: true }, + }; + } + default: { + return state; + } + } +}; + +const hasSuccessfullyCreatedJob = (jobId: string) => ( + jobSetupResponses: SetupMlModuleResponsePayload['jobs'] +) => + jobSetupResponses.filter( + (jobSetupResponse) => + jobSetupResponse.id === jobId && jobSetupResponse.success && !jobSetupResponse.error + ).length > 0; + +const hasSuccessfullyStartedDatafeed = (datafeedId: string) => ( + datafeedSetupResponses: SetupMlModuleResponsePayload['datafeeds'] +) => + datafeedSetupResponses.filter( + (datafeedSetupResponse) => + datafeedSetupResponse.id === datafeedId && + datafeedSetupResponse.success && + datafeedSetupResponse.started && + !datafeedSetupResponse.error + ).length > 0; + +const getJobStatus = (jobId: string) => ( + jobSummaries: FetchJobStatusResponsePayload +): JobStatus => { + return ( + jobSummaries + .filter((jobSummary) => jobSummary.id === jobId) + .map( + (jobSummary): JobStatus => { + if (jobSummary.jobState === 'failed' || jobSummary.datafeedState === '') { + return 'failed'; + } else if ( + jobSummary.jobState === 'closed' && + jobSummary.datafeedState === 'stopped' && + jobSummary.fullJob && + jobSummary.fullJob.finished_time != null + ) { + return 'finished'; + } else if ( + jobSummary.jobState === 'closed' || + jobSummary.jobState === 'closing' || + jobSummary.datafeedState === 'stopped' + ) { + return 'stopped'; + } else if (jobSummary.jobState === 'opening') { + return 'initializing'; + } else if (jobSummary.jobState === 'opened' && jobSummary.datafeedState === 'started') { + return 'started'; + } + + return 'unknown'; + } + )[0] || 'missing' + ); +}; + +const getSetupStatus = (everyJobStatus: Record) => ( + previousSetupStatus: SetupStatus +): SetupStatus => { + return Object.entries(everyJobStatus).reduce( + (setupStatus, [, jobStatus]) => { + if (jobStatus === 'missing') { + return { type: 'required' }; + } else if (setupStatus.type === 'required' || setupStatus.type === 'succeeded') { + return setupStatus; + } else if (setupStatus.type === 'skipped' || isJobStatusWithResults(jobStatus)) { + return { + type: 'skipped', + // preserve newlyCreated status + newlyCreated: setupStatus.type === 'skipped' && setupStatus.newlyCreated, + }; + } + + return setupStatus; + }, + previousSetupStatus + ); +}; + +const hasError = ( + value: Value +): value is MandatoryProperty => value.error != null; + +export const useModuleStatus = (jobTypes: JobType[]) => { + return useReducer(createStatusReducer(jobTypes), { jobTypes }, createInitialState); +}; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts new file mode 100644 index 00000000000000..a9f2671de82598 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts @@ -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 { + ValidateLogEntryDatasetsResponsePayload, + ValidationIndicesResponsePayload, +} from '../../../common/http_api/log_analysis'; +import { DatasetFilter } from '../../../common/infra_ml'; +import { DeleteJobsResponsePayload } from './api/ml_cleanup'; +import { FetchJobStatusResponsePayload } from './api/ml_get_jobs_summary_api'; +import { GetMlModuleResponsePayload } from './api/ml_get_module'; +import { SetupMlModuleResponsePayload } from './api/ml_setup_module_api'; + +export { JobModelSizeStats, JobSummary } from './api/ml_get_jobs_summary_api'; + +export interface ModuleDescriptor { + moduleId: string; + moduleName: string; + moduleDescription: string; + jobTypes: JobType[]; + bucketSpan: number; + getJobIds: (spaceId: string, sourceId: string) => Record; + getJobSummary: (spaceId: string, sourceId: string) => Promise; + getModuleDefinition: () => Promise; + setUpModule: ( + start: number | undefined, + end: number | undefined, + datasetFilter: DatasetFilter, + sourceConfiguration: ModuleSourceConfiguration, + partitionField?: string + ) => Promise; + cleanUpModule: (spaceId: string, sourceId: string) => Promise; + validateSetupIndices: ( + indices: string[], + timestampField: string + ) => Promise; + validateSetupDatasets: ( + indices: string[], + timestampField: string, + startTime: number, + endTime: number + ) => Promise; +} + +export interface ModuleSourceConfiguration { + indices: string[]; + sourceId: string; + spaceId: string; + timestampField: string; +} + +interface ManyCategoriesWarningReason { + type: 'manyCategories'; + categoriesDocumentRatio: number; +} + +interface ManyDeadCategoriesWarningReason { + type: 'manyDeadCategories'; + deadCategoriesRatio: number; +} + +interface ManyRareCategoriesWarningReason { + type: 'manyRareCategories'; + rareCategoriesRatio: number; +} + +interface NoFrequentCategoriesWarningReason { + type: 'noFrequentCategories'; +} + +interface SingleCategoryWarningReason { + type: 'singleCategory'; +} + +export type CategoryQualityWarningReason = + | ManyCategoriesWarningReason + | ManyDeadCategoriesWarningReason + | ManyRareCategoriesWarningReason + | NoFrequentCategoriesWarningReason + | SingleCategoryWarningReason; + +export type CategoryQualityWarningReasonType = CategoryQualityWarningReason['type']; + +export interface CategoryQualityWarning { + type: 'categoryQualityWarning'; + jobId: string; + reasons: CategoryQualityWarningReason[]; +} + +export type QualityWarning = CategoryQualityWarning; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts new file mode 100644 index 00000000000000..0dfe3b301f2400 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts @@ -0,0 +1,289 @@ +/* + * 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 { isEqual } from 'lodash'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { usePrevious } from 'react-use'; +import { + combineDatasetFilters, + DatasetFilter, + filterDatasetFilter, + isExampleDataIndex, +} from '../../../common/infra_ml'; +import { + AvailableIndex, + ValidationIndicesError, + ValidationUIError, +} from '../../components/logging/log_analysis_setup/initial_configuration_step'; +import { useTrackedPromise } from '../../utils/use_tracked_promise'; +import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types'; + +type SetupHandler = ( + indices: string[], + startTime: number | undefined, + endTime: number | undefined, + datasetFilter: DatasetFilter +) => void; + +interface AnalysisSetupStateArguments { + cleanUpAndSetUpModule: SetupHandler; + moduleDescriptor: ModuleDescriptor; + setUpModule: SetupHandler; + sourceConfiguration: ModuleSourceConfiguration; +} + +const fourWeeksInMs = 86400000 * 7 * 4; + +export const useAnalysisSetupState = ({ + cleanUpAndSetUpModule, + moduleDescriptor: { validateSetupDatasets, validateSetupIndices }, + setUpModule, + sourceConfiguration, +}: AnalysisSetupStateArguments) => { + const [startTime, setStartTime] = useState(Date.now() - fourWeeksInMs); + const [endTime, setEndTime] = useState(undefined); + + const isTimeRangeValid = useMemo( + () => (startTime != null && endTime != null ? startTime < endTime : true), + [endTime, startTime] + ); + + const [validatedIndices, setValidatedIndices] = useState( + sourceConfiguration.indices.map((indexName) => ({ + name: indexName, + validity: 'unknown' as const, + })) + ); + + const updateIndicesWithValidationErrors = useCallback( + (validationErrors: ValidationIndicesError[]) => + setValidatedIndices((availableIndices) => + availableIndices.map((previousAvailableIndex) => { + const indexValiationErrors = validationErrors.filter( + ({ index }) => index === previousAvailableIndex.name + ); + + if (indexValiationErrors.length > 0) { + return { + validity: 'invalid', + name: previousAvailableIndex.name, + errors: indexValiationErrors, + }; + } else if (previousAvailableIndex.validity === 'valid') { + return { + ...previousAvailableIndex, + validity: 'valid', + errors: [], + }; + } else { + return { + validity: 'valid', + name: previousAvailableIndex.name, + isSelected: !isExampleDataIndex(previousAvailableIndex.name), + availableDatasets: [], + datasetFilter: { + type: 'includeAll' as const, + }, + }; + } + }) + ), + [] + ); + + const updateIndicesWithAvailableDatasets = useCallback( + (availableDatasets: Array<{ indexName: string; datasets: string[] }>) => + setValidatedIndices((availableIndices) => + availableIndices.map((previousAvailableIndex) => { + if (previousAvailableIndex.validity !== 'valid') { + return previousAvailableIndex; + } + + const availableDatasetsForIndex = availableDatasets.filter( + ({ indexName }) => indexName === previousAvailableIndex.name + ); + const newAvailableDatasets = availableDatasetsForIndex.flatMap( + ({ datasets }) => datasets + ); + + // filter out datasets that have disappeared if this index' datasets were updated + const newDatasetFilter: DatasetFilter = + availableDatasetsForIndex.length > 0 + ? filterDatasetFilter(previousAvailableIndex.datasetFilter, (dataset) => + newAvailableDatasets.includes(dataset) + ) + : previousAvailableIndex.datasetFilter; + + return { + ...previousAvailableIndex, + availableDatasets: newAvailableDatasets, + datasetFilter: newDatasetFilter, + }; + }) + ), + [] + ); + + const validIndexNames = useMemo( + () => validatedIndices.filter((index) => index.validity === 'valid').map((index) => index.name), + [validatedIndices] + ); + + const selectedIndexNames = useMemo( + () => + validatedIndices + .filter((index) => index.validity === 'valid' && index.isSelected) + .map((i) => i.name), + [validatedIndices] + ); + + const datasetFilter = useMemo( + () => + validatedIndices + .flatMap((validatedIndex) => + validatedIndex.validity === 'valid' + ? validatedIndex.datasetFilter + : { type: 'includeAll' as const } + ) + .reduce(combineDatasetFilters, { type: 'includeAll' as const }), + [validatedIndices] + ); + + const [validateIndicesRequest, validateIndices] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + return await validateSetupIndices( + sourceConfiguration.indices, + sourceConfiguration.timestampField + ); + }, + onResolve: ({ data: { errors } }) => { + updateIndicesWithValidationErrors(errors); + }, + onReject: () => { + setValidatedIndices([]); + }, + }, + [sourceConfiguration.indices, sourceConfiguration.timestampField] + ); + + const [validateDatasetsRequest, validateDatasets] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + if (validIndexNames.length === 0) { + return { data: { datasets: [] } }; + } + + return await validateSetupDatasets( + validIndexNames, + sourceConfiguration.timestampField, + startTime ?? 0, + endTime ?? Date.now() + ); + }, + onResolve: ({ data: { datasets } }) => { + updateIndicesWithAvailableDatasets(datasets); + }, + }, + [validIndexNames, sourceConfiguration.timestampField, startTime, endTime] + ); + + const setUp = useCallback(() => { + return setUpModule(selectedIndexNames, startTime, endTime, datasetFilter); + }, [setUpModule, selectedIndexNames, startTime, endTime, datasetFilter]); + + const cleanUpAndSetUp = useCallback(() => { + return cleanUpAndSetUpModule(selectedIndexNames, startTime, endTime, datasetFilter); + }, [cleanUpAndSetUpModule, selectedIndexNames, startTime, endTime, datasetFilter]); + + const isValidating = useMemo( + () => validateIndicesRequest.state === 'pending' || validateDatasetsRequest.state === 'pending', + [validateDatasetsRequest.state, validateIndicesRequest.state] + ); + + const validationErrors = useMemo(() => { + if (isValidating) { + return []; + } + + return [ + // validate request status + ...(validateIndicesRequest.state === 'rejected' || + validateDatasetsRequest.state === 'rejected' + ? [{ error: 'NETWORK_ERROR' as const }] + : []), + // validation request results + ...validatedIndices.reduce((errors, index) => { + return index.validity === 'invalid' && selectedIndexNames.includes(index.name) + ? [...errors, ...index.errors] + : errors; + }, []), + // index count + ...(selectedIndexNames.length === 0 ? [{ error: 'TOO_FEW_SELECTED_INDICES' as const }] : []), + // time range + ...(!isTimeRangeValid ? [{ error: 'INVALID_TIME_RANGE' as const }] : []), + ]; + }, [ + isValidating, + validateIndicesRequest.state, + validateDatasetsRequest.state, + validatedIndices, + selectedIndexNames, + isTimeRangeValid, + ]); + + const prevStartTime = usePrevious(startTime); + const prevEndTime = usePrevious(endTime); + const prevValidIndexNames = usePrevious(validIndexNames); + + useEffect(() => { + if (!isTimeRangeValid) { + return; + } + + validateIndices(); + }, [isTimeRangeValid, validateIndices]); + + useEffect(() => { + if (!isTimeRangeValid) { + return; + } + + if ( + startTime !== prevStartTime || + endTime !== prevEndTime || + !isEqual(validIndexNames, prevValidIndexNames) + ) { + validateDatasets(); + } + }, [ + endTime, + isTimeRangeValid, + prevEndTime, + prevStartTime, + prevValidIndexNames, + startTime, + validIndexNames, + validateDatasets, + ]); + + return { + cleanUpAndSetUp, + datasetFilter, + endTime, + isValidating, + selectedIndexNames, + setEndTime, + setStartTime, + setUp, + startTime, + validatedIndices, + setValidatedIndices, + validationErrors, + }; +}; diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module.tsx b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module.tsx new file mode 100644 index 00000000000000..9c065f3e91bc49 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module.tsx @@ -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 createContainer from 'constate'; +import { useMemo } from 'react'; +import { useInfraMLModule } from '../../infra_ml_module'; +import { useInfraMLModuleConfiguration } from '../../infra_ml_module_configuration'; +import { useInfraMLModuleDefinition } from '../../infra_ml_module_definition'; +import { ModuleSourceConfiguration } from '../../infra_ml_module_types'; +import { metricHostsModule } from './module_descriptor'; + +export const useMetricHostsModule = ({ + indexPattern, + sourceId, + spaceId, + timestampField, +}: { + indexPattern: string; + sourceId: string; + spaceId: string; + timestampField: string; +}) => { + const sourceConfiguration: ModuleSourceConfiguration = useMemo( + () => ({ + indices: indexPattern.split(','), + sourceId, + spaceId, + timestampField, + }), + [indexPattern, sourceId, spaceId, timestampField] + ); + + const infraMLModule = useInfraMLModule({ + moduleDescriptor: metricHostsModule, + sourceConfiguration, + }); + + const { getIsJobConfigurationOutdated } = useInfraMLModuleConfiguration({ + sourceConfiguration, + moduleDescriptor: metricHostsModule, + }); + + const { fetchModuleDefinition, getIsJobDefinitionOutdated } = useInfraMLModuleDefinition({ + sourceConfiguration, + moduleDescriptor: metricHostsModule, + }); + + const hasOutdatedJobConfigurations = useMemo( + () => infraMLModule.jobSummaries.some(getIsJobConfigurationOutdated), + [getIsJobConfigurationOutdated, infraMLModule.jobSummaries] + ); + + const hasOutdatedJobDefinitions = useMemo( + () => infraMLModule.jobSummaries.some(getIsJobDefinitionOutdated), + [getIsJobDefinitionOutdated, infraMLModule.jobSummaries] + ); + + const hasStoppedJobs = useMemo( + () => + Object.values(infraMLModule.jobStatus).some( + (currentJobStatus) => currentJobStatus === 'stopped' + ), + [infraMLModule.jobStatus] + ); + + return { + ...infraMLModule, + fetchModuleDefinition, + hasOutdatedJobConfigurations, + hasOutdatedJobDefinitions, + hasStoppedJobs, + }; +}; + +export const [MetricHostsModuleProvider, useMetricHostsModuleContext] = createContainer( + useMetricHostsModule +); diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts new file mode 100644 index 00000000000000..cec87fb1144e33 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts @@ -0,0 +1,126 @@ +/* + * 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'; +import { ModuleDescriptor, ModuleSourceConfiguration } from '../../infra_ml_module_types'; +import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup'; +import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api'; +import { callGetMlModuleAPI } from '../../api/ml_get_module'; +import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api'; +import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices'; +import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets'; +import { + metricsHostsJobTypes, + getJobId, + MetricsHostsJobType, + DatasetFilter, + bucketSpan, + partitionField, +} from '../../../../../common/infra_ml'; + +const moduleId = 'metrics_ui_hosts'; +const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', { + defaultMessage: 'Metrics anomanly detection', +}); +const moduleDescription = i18n.translate('xpack.infra.ml.metricsHostModuleDescription', { + defaultMessage: 'Use Machine Learning to automatically detect anomalous log entry rates.', +}); + +const getJobIds = (spaceId: string, sourceId: string) => + metricsHostsJobTypes.reduce( + (accumulatedJobIds, jobType) => ({ + ...accumulatedJobIds, + [jobType]: getJobId(spaceId, sourceId, jobType), + }), + {} as Record + ); + +const getJobSummary = async (spaceId: string, sourceId: string) => { + const response = await callJobsSummaryAPI(spaceId, sourceId, metricsHostsJobTypes); + const jobIds = Object.values(getJobIds(spaceId, sourceId)); + + return response.filter((jobSummary) => jobIds.includes(jobSummary.id)); +}; + +const getModuleDefinition = async () => { + return await callGetMlModuleAPI(moduleId); +}; + +const setUpModule = async ( + start: number | undefined, + end: number | undefined, + datasetFilter: DatasetFilter, + { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, + pField?: string +) => { + const indexNamePattern = indices.join(','); + const jobIds = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out']; + const jobOverrides = jobIds.map((id) => ({ + job_id: id, + data_description: { + time_field: timestampField, + }, + custom_settings: { + metrics_source_config: { + indexPattern: indexNamePattern, + timestampField, + bucketSpan, + }, + }, + })); + + return callSetupMlModuleAPI( + moduleId, + start, + end, + spaceId, + sourceId, + indexNamePattern, + jobOverrides, + [] + ); +}; + +const cleanUpModule = async (spaceId: string, sourceId: string) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes); +}; + +const validateSetupIndices = async (indices: string[], timestampField: string) => { + return await callValidateIndicesAPI(indices, [ + { + name: timestampField, + validTypes: ['date'], + }, + { + name: partitionField, + validTypes: ['keyword'], + }, + ]); +}; + +const validateSetupDatasets = async ( + indices: string[], + timestampField: string, + startTime: number, + endTime: number +) => { + return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime); +}; + +export const metricHostsModule: ModuleDescriptor = { + moduleId, + moduleName, + moduleDescription, + jobTypes: metricsHostsJobTypes, + bucketSpan, + getJobIds, + getJobSummary, + getModuleDefinition, + setUpModule, + cleanUpModule, + validateSetupDatasets, + validateSetupIndices, +}; diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module.tsx b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module.tsx new file mode 100644 index 00000000000000..07c8ab02f17ee1 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module.tsx @@ -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 createContainer from 'constate'; +import { useMemo } from 'react'; +import { useInfraMLModule } from '../../infra_ml_module'; +import { useInfraMLModuleConfiguration } from '../../infra_ml_module_configuration'; +import { useInfraMLModuleDefinition } from '../../infra_ml_module_definition'; +import { ModuleSourceConfiguration } from '../../infra_ml_module_types'; +import { metricHostsModule } from './module_descriptor'; + +export const useMetricK8sModule = ({ + indexPattern, + sourceId, + spaceId, + timestampField, +}: { + indexPattern: string; + sourceId: string; + spaceId: string; + timestampField: string; +}) => { + const sourceConfiguration: ModuleSourceConfiguration = useMemo( + () => ({ + indices: indexPattern.split(','), + sourceId, + spaceId, + timestampField, + }), + [indexPattern, sourceId, spaceId, timestampField] + ); + + const infraMLModule = useInfraMLModule({ + moduleDescriptor: metricHostsModule, + sourceConfiguration, + }); + + const { getIsJobConfigurationOutdated } = useInfraMLModuleConfiguration({ + sourceConfiguration, + moduleDescriptor: metricHostsModule, + }); + + const { fetchModuleDefinition, getIsJobDefinitionOutdated } = useInfraMLModuleDefinition({ + sourceConfiguration, + moduleDescriptor: metricHostsModule, + }); + + const hasOutdatedJobConfigurations = useMemo( + () => infraMLModule.jobSummaries.some(getIsJobConfigurationOutdated), + [getIsJobConfigurationOutdated, infraMLModule.jobSummaries] + ); + + const hasOutdatedJobDefinitions = useMemo( + () => infraMLModule.jobSummaries.some(getIsJobDefinitionOutdated), + [getIsJobDefinitionOutdated, infraMLModule.jobSummaries] + ); + + const hasStoppedJobs = useMemo( + () => + Object.values(infraMLModule.jobStatus).some( + (currentJobStatus) => currentJobStatus === 'stopped' + ), + [infraMLModule.jobStatus] + ); + + return { + ...infraMLModule, + fetchModuleDefinition, + hasOutdatedJobConfigurations, + hasOutdatedJobDefinitions, + hasStoppedJobs, + }; +}; + +export const [MetricK8sModuleProvider, useMetricK8sModuleContext] = createContainer( + useMetricK8sModule +); diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts new file mode 100644 index 00000000000000..cbcff1c307af6e --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts @@ -0,0 +1,129 @@ +/* + * 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'; +import { ModuleDescriptor, ModuleSourceConfiguration } from '../../infra_ml_module_types'; +import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup'; +import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api'; +import { callGetMlModuleAPI } from '../../api/ml_get_module'; +import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api'; +import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices'; +import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets'; +import { + metricsK8SJobTypes, + getJobId, + MetricK8sJobType, + DatasetFilter, + bucketSpan, + partitionField, +} from '../../../../../common/infra_ml'; + +const moduleId = 'metrics_ui_k8s'; +const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', { + defaultMessage: 'Metrics anomanly detection', +}); +const moduleDescription = i18n.translate('xpack.infra.ml.metricsHostModuleDescription', { + defaultMessage: 'Use Machine Learning to automatically detect anomalous log entry rates.', +}); + +const getJobIds = (spaceId: string, sourceId: string) => + metricsK8SJobTypes.reduce( + (accumulatedJobIds, jobType) => ({ + ...accumulatedJobIds, + [jobType]: getJobId(spaceId, sourceId, jobType), + }), + {} as Record + ); + +const getJobSummary = async (spaceId: string, sourceId: string) => { + const response = await callJobsSummaryAPI(spaceId, sourceId, metricsK8SJobTypes); + const jobIds = Object.values(getJobIds(spaceId, sourceId)); + + return response.filter((jobSummary) => jobIds.includes(jobSummary.id)); +}; + +const getModuleDefinition = async () => { + return await callGetMlModuleAPI(moduleId); +}; + +const setUpModule = async ( + start: number | undefined, + end: number | undefined, + datasetFilter: DatasetFilter, + { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, + pField?: string +) => { + const indexNamePattern = indices.join(','); + const jobIds = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out']; + const jobOverrides = jobIds.map((id) => ({ + job_id: id, + analysis_config: { + bucket_span: `${bucketSpan}ms`, + }, + data_description: { + time_field: timestampField, + }, + custom_settings: { + metrics_source_config: { + indexPattern: indexNamePattern, + timestampField, + bucketSpan, + }, + }, + })); + + return callSetupMlModuleAPI( + moduleId, + start, + end, + spaceId, + sourceId, + indexNamePattern, + jobOverrides, + [] + ); +}; + +const cleanUpModule = async (spaceId: string, sourceId: string) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes); +}; + +const validateSetupIndices = async (indices: string[], timestampField: string) => { + return await callValidateIndicesAPI(indices, [ + { + name: timestampField, + validTypes: ['date'], + }, + { + name: partitionField, + validTypes: ['keyword'], + }, + ]); +}; + +const validateSetupDatasets = async ( + indices: string[], + timestampField: string, + startTime: number, + endTime: number +) => { + return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime); +}; + +export const metricHostsModule: ModuleDescriptor = { + moduleId, + moduleName, + moduleDescription, + jobTypes: metricsK8SJobTypes, + bucketSpan, + getJobIds, + getJobSummary, + getModuleDefinition, + setUpModule, + cleanUpModule, + validateSetupDatasets, + validateSetupIndices, +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index 3b3ed80f9e7311..ac2c87248ae779 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -38,6 +38,8 @@ import { MetricsAlertDropdown } from '../../alerting/metric_threshold/components import { SavedView } from '../../containers/saved_view/saved_view'; import { SourceConfigurationFields } from '../../graphql/types'; import { AlertPrefillProvider } from '../../alerting/use_alert_prefill'; +import { InfraMLCapabilitiesProvider } from '../../containers/ml/infra_ml_capabilities'; +import { AnomalyDetectionFlyout } from './inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout'; const ADD_DATA_LABEL = i18n.translate('xpack.infra.metricsHeaderAddDataButtonLabel', { defaultMessage: 'Add data', @@ -55,110 +57,118 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => { - - + + + - + -
+ - - - - - - - - - - - - {ADD_DATA_LABEL} - - - - - - - - ( - - {({ configuration, createDerivedIndexPattern }) => ( - - - {configuration ? ( - - ) : ( - - )} - - )} - + } )} - /> - - - + > + + + + + + + + + + + + + + {ADD_DATA_LABEL} + + + + + + + + ( + + {({ configuration, createDerivedIndexPattern }) => ( + + + {configuration ? ( + + ) : ( + + )} + + )} + + )} + /> + + + + diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx new file mode 100644 index 00000000000000..b063713fa2c971 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx @@ -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 React, { useState, useCallback } from 'react'; +import { EuiButtonEmpty, EuiFlyout } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { FlyoutHome } from './flyout_home'; +import { JobSetupScreen } from './job_setup_screen'; +import { useInfraMLCapabilities } from '../../../../../../containers/ml/infra_ml_capabilities'; +import { MetricHostsModuleProvider } from '../../../../../../containers/ml/modules/metrics_hosts/module'; +import { MetricK8sModuleProvider } from '../../../../../../containers/ml/modules/metrics_k8s/module'; +import { useSourceViaHttp } from '../../../../../../containers/source/use_source_via_http'; +import { useActiveKibanaSpace } from '../../../../../../hooks/use_kibana_space'; + +export const AnomalyDetectionFlyout = () => { + const { hasInfraMLSetupCapabilities } = useInfraMLCapabilities(); + const [showFlyout, setShowFlyout] = useState(false); + const [screenName, setScreenName] = useState<'home' | 'setup'>('home'); + const [screenParams, setScreenParams] = useState(null); + const { source } = useSourceViaHttp({ + sourceId: 'default', + type: 'metrics', + }); + + const { space } = useActiveKibanaSpace(); + + const openFlyout = useCallback(() => { + setScreenName('home'); + setShowFlyout(true); + }, []); + + const openJobSetup = useCallback( + (jobType: 'hosts' | 'kubernetes') => { + setScreenName('setup'); + setScreenParams({ jobType }); + }, + [setScreenName] + ); + + const closeFlyout = useCallback(() => { + setShowFlyout(false); + }, []); + + if (source?.configuration.metricAlias == null || space == null) { + return null; + } + + return ( + <> + + + + {showFlyout && ( + + + + {screenName === 'home' && ( + + )} + {screenName === 'setup' && ( + + )} + + + + )} + + ); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx new file mode 100644 index 00000000000000..9cf898b6843363 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx @@ -0,0 +1,333 @@ +/* + * 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, { useState, useCallback, useEffect } from 'react'; +import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiTabs, EuiTab, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiText, EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { EuiCallOut } from '@elastic/eui'; +import { EuiButton } from '@elastic/eui'; +import { EuiButtonEmpty } from '@elastic/eui'; +import moment from 'moment'; +import { useInfraMLCapabilitiesContext } from '../../../../../../containers/ml/infra_ml_capabilities'; +import { SubscriptionSplashContent } from './subscription_splash_content'; +import { + MissingResultsPrivilegesPrompt, + MissingSetupPrivilegesPrompt, +} from '../../../../../../components/logging/log_analysis_setup'; +import { useMetricHostsModuleContext } from '../../../../../../containers/ml/modules/metrics_hosts/module'; +import { useMetricK8sModuleContext } from '../../../../../../containers/ml/modules/metrics_k8s/module'; +import { LoadingPage } from '../../../../../../components/loading_page'; +import { useLinkProps } from '../../../../../../hooks/use_link_props'; + +interface Props { + hasSetupCapabilities: boolean; + goToSetup(type: 'hosts' | 'kubernetes'): void; +} + +export const FlyoutHome = (props: Props) => { + const [tab, setTab] = useState<'jobs' | 'anomalies'>('jobs'); + const { goToSetup } = props; + const { + fetchJobStatus: fetchHostJobStatus, + setupStatus: hostSetupStatus, + jobSummaries: hostJobSummaries, + } = useMetricHostsModuleContext(); + const { + fetchJobStatus: fetchK8sJobStatus, + setupStatus: k8sSetupStatus, + jobSummaries: k8sJobSummaries, + } = useMetricK8sModuleContext(); + const { + hasInfraMLCapabilites, + hasInfraMLReadCapabilities, + hasInfraMLSetupCapabilities, + } = useInfraMLCapabilitiesContext(); + + const createHosts = useCallback(() => { + goToSetup('hosts'); + }, [goToSetup]); + + const createK8s = useCallback(() => { + goToSetup('kubernetes'); + }, [goToSetup]); + + const goToJobs = useCallback(() => { + setTab('jobs'); + }, []); + + const jobIds = [ + ...(k8sJobSummaries || []).map((k) => k.id), + ...(hostJobSummaries || []).map((h) => h.id), + ]; + const anomaliesUrl = useLinkProps({ + app: 'ml', + pathname: `/explorer?_g=${createResultsUrl(jobIds)}`, + }); + + useEffect(() => { + if (hasInfraMLReadCapabilities) { + fetchHostJobStatus(); + fetchK8sJobStatus(); + } + }, [fetchK8sJobStatus, fetchHostJobStatus, hasInfraMLReadCapabilities]); + + if (!hasInfraMLCapabilites) { + return ; + } else if (!hasInfraMLReadCapabilities) { + return ; + } else if (hostSetupStatus.type === 'initializing' || k8sSetupStatus.type === 'initializing') { + return ( + + ); + } else if (!hasInfraMLSetupCapabilities) { + return ; + } else { + return ( + <> + + +

+ +

+
+
+ + + + + + + + + + + + {hostJobSummaries.length > 0 && ( + <> + 0} + hasK8sJobs={k8sJobSummaries.length > 0} + /> + + + )} + {tab === 'jobs' && ( + 0} + hasK8sJobs={k8sJobSummaries.length > 0} + hasSetupCapabilities={props.hasSetupCapabilities} + createHosts={createHosts} + createK8s={createK8s} + /> + )} + + + ); + } +}; + +interface CalloutProps { + hasHostJobs: boolean; + hasK8sJobs: boolean; +} +const JobsEnabledCallout = (props: CalloutProps) => { + let target = ''; + if (props.hasHostJobs && props.hasK8sJobs) { + target = `${i18n.translate('xpack.infra.ml.anomalyFlyout.create.hostTitle', { + defaultMessage: 'Hosts', + })} and ${i18n.translate('xpack.infra.ml.anomalyFlyout.create.k8sSuccessTitle', { + defaultMessage: 'Kubernetes', + })}`; + } else if (props.hasHostJobs) { + target = i18n.translate('xpack.infra.ml.anomalyFlyout.create.hostSuccessTitle', { + defaultMessage: 'Hosts', + }); + } else if (props.hasK8sJobs) { + target = i18n.translate('xpack.infra.ml.anomalyFlyout.create.k8sSuccessTitle', { + defaultMessage: 'Kubernetes', + }); + } + + const manageJobsLinkProps = useLinkProps({ + app: 'ml', + pathname: '/jobs', + }); + + return ( + <> + + } + iconType="check" + /> + + + + + + ); +}; + +interface CreateJobTab { + hasSetupCapabilities: boolean; + hasHostJobs: boolean; + hasK8sJobs: boolean; + createHosts(): void; + createK8s(): void; +} + +const CreateJobTab = (props: CreateJobTab) => { + return ( + <> +
+ +

+ +

+
+ +

+ +

+
+
+ + + + + } + // title="Hosts" + title={ + + } + description={ + + } + footer={ + <> + {props.hasHostJobs && ( + + + + )} + {!props.hasHostJobs && ( + + + + )} + + } + /> + + + } + title={ + + } + description={ + + } + footer={ + <> + {props.hasK8sJobs && ( + + + + )} + {!props.hasK8sJobs && ( + + + + )} + + } + /> + + + + ); +}; + +function createResultsUrl(jobIds: string[], mode = 'absolute') { + const idString = jobIds.map((j) => `'${j}'`).join(','); + let path = ''; + + const from = moment().subtract(4, 'weeks').toISOString(); + const to = moment().toISOString(); + + path += `(ml:(jobIds:!(${idString}))`; + path += `,refreshInterval:(display:Off,pause:!f,value:0),time:(from:'${from}'`; + path += `,to:'${to}'`; + if (mode === 'invalid') { + path += `,mode:invalid`; + } + path += "))&_a=(query:(query_string:(analyze_wildcard:!t,query:'*')))"; + + return path; +} diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx new file mode 100644 index 00000000000000..730cd7b6e9ef5b --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx @@ -0,0 +1,277 @@ +/* + * 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, { useState, useCallback, useMemo, useEffect } from 'react'; +import { EuiForm, EuiDescribedFormGroup, EuiFormRow } from '@elastic/eui'; +import { EuiText, EuiSpacer } from '@elastic/eui'; +import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFlyoutFooter } from '@elastic/eui'; +import { EuiButton } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; +import moment, { Moment } from 'moment'; +import { EuiComboBox } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { useSourceViaHttp } from '../../../../../../containers/source/use_source_via_http'; +import { useMetricK8sModuleContext } from '../../../../../../containers/ml/modules/metrics_k8s/module'; +import { useMetricHostsModuleContext } from '../../../../../../containers/ml/modules/metrics_hosts/module'; +import { FixedDatePicker } from '../../../../../../components/fixed_datepicker'; + +interface Props { + jobType: 'hosts' | 'kubernetes'; + closeFlyout(): void; + goHome(): void; +} + +export const JobSetupScreen = (props: Props) => { + const [now] = useState(() => moment()); + const { goHome } = props; + const [startDate, setStartDate] = useState(now.clone().subtract(4, 'weeks')); + const [partitionField, setPartitionField] = useState(null); + const h = useMetricHostsModuleContext(); + const k = useMetricK8sModuleContext(); + const { createDerivedIndexPattern } = useSourceViaHttp({ + sourceId: 'default', + type: 'metrics', + }); + + const indicies = h.sourceConfiguration.indices; + + const setupStatus = useMemo(() => { + if (props.jobType === 'kubernetes') { + return k.setupStatus; + } else { + return h.setupStatus; + } + }, [props.jobType, k.setupStatus, h.setupStatus]); + + const cleanUpAndSetUpModule = useMemo(() => { + if (props.jobType === 'kubernetes') { + return k.cleanUpAndSetUpModule; + } else { + return h.cleanUpAndSetUpModule; + } + }, [props.jobType, k.cleanUpAndSetUpModule, h.cleanUpAndSetUpModule]); + + const setUpModule = useMemo(() => { + if (props.jobType === 'kubernetes') { + return k.setUpModule; + } else { + return h.setUpModule; + } + }, [props.jobType, k.setUpModule, h.setUpModule]); + + const hasSummaries = useMemo(() => { + if (props.jobType === 'kubernetes') { + return k.jobSummaries.length > 0; + } else { + return h.jobSummaries.length > 0; + } + }, [props.jobType, k.jobSummaries, h.jobSummaries]); + + const derivedIndexPattern = useMemo(() => createDerivedIndexPattern('metrics'), [ + createDerivedIndexPattern, + ]); + + const updateStart = useCallback((date: Moment) => { + setStartDate(date); + }, []); + + const createJobs = useCallback(() => { + if (hasSummaries) { + cleanUpAndSetUpModule( + indicies, + moment(startDate).toDate().getTime(), + undefined, + { type: 'includeAll' }, + partitionField ? partitionField[0] : undefined + ); + } else { + setUpModule( + indicies, + moment(startDate).toDate().getTime(), + undefined, + { type: 'includeAll' }, + partitionField ? partitionField[0] : undefined + ); + } + }, [cleanUpAndSetUpModule, setUpModule, hasSummaries, indicies, partitionField, startDate]); + + const onPartitionFieldChange = useCallback((value: Array<{ label: string }>) => { + setPartitionField(value.map((v) => v.label)); + }, []); + + useEffect(() => { + if (props.jobType === 'kubernetes') { + setPartitionField(['kubernetes.namespace']); + } + }, [props.jobType]); + + useEffect(() => { + if (setupStatus.type === 'succeeded') { + goHome(); + } + }, [setupStatus, goHome]); + + return ( + <> + + +

+ +

+
+
+ + {setupStatus.type === 'pending' ? ( + + + + + + + + + ) : setupStatus.type === 'failed' ? ( + <> + + + + + + + ) : ( + <> + +

+ +

+
+ + + + + + + } + description={ + + } + > + + } + > + + + + + + + + } + description={ + + } + > + + } + compressed + > + ({ label: p })) : undefined + } + options={derivedIndexPattern.fields + .filter((f) => f.aggregatable && f.type === 'string') + .map((f) => ({ label: f.name }))} + onChange={onPartitionFieldChange} + isClearable={true} + /> + + + + + )} +
+ + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx new file mode 100644 index 00000000000000..f07c37f5e7ea2a --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx @@ -0,0 +1,172 @@ +/* + * 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, { useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiPage, + EuiPageBody, + EuiPageContent, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiTitle, + EuiText, + EuiButton, + EuiButtonEmpty, + EuiImage, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { LoadingPage } from '../../../../../../components/loading_page'; +import { useTrialStatus } from '../../../../../../hooks/use_trial_status'; +import { useKibana } from '../../../../../../../../../../src/plugins/kibana_react/public'; +import { euiStyled } from '../../../../../../../../observability/public'; +import { HttpStart } from '../../../../../../../../../../src/core/public'; + +export const SubscriptionSplashContent: React.FC = () => { + const { services } = useKibana<{ http: HttpStart }>(); + const { loadState, isTrialAvailable, checkTrialAvailability } = useTrialStatus(); + + useEffect(() => { + checkTrialAvailability(); + }, [checkTrialAvailability]); + + if (loadState === 'pending') { + return ( + + ); + } + + const canStartTrial = isTrialAvailable && loadState === 'resolved'; + + let title; + let description; + let cta; + + if (canStartTrial) { + title = ( + + ); + + description = ( + + ); + + cta = ( + + + + ); + } else { + title = ( + + ); + + description = ( + + ); + + cta = ( + + + + ); + } + + return ( + + + + + + +

{title}

+
+ + +

{description}

+
+ +
{cta}
+
+ + + +
+ + +

+ +

+
+ + + +
+
+
+
+ ); +}; + +const SubscriptionPage = euiStyled(EuiPage)` + height: 100% +`; + +const SubscriptionPageContent = euiStyled(EuiPageContent)` + max-width: 768px !important; +`; + +const SubscriptionPageFooter = euiStyled.div` + background: ${(props) => props.theme.eui.euiColorLightestShade}; + margin: 0 -${(props) => props.theme.eui.paddingSizes.l} -${(props) => + props.theme.eui.paddingSizes.l}; + padding: ${(props) => props.theme.eui.paddingSizes.l}; +`; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_hosts_anomalies.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_hosts_anomalies.ts new file mode 100644 index 00000000000000..f755057d0b76d5 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_hosts_anomalies.ts @@ -0,0 +1,318 @@ +/* + * 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 { useMemo, useState, useCallback, useEffect, useReducer } from 'react'; +import { + INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH, + Sort, + Pagination, + PaginationCursor, + getMetricsHostsAnomaliesRequestPayloadRT, + MetricsHostsAnomaly, + getMetricsHostsAnomaliesSuccessReponsePayloadRT, +} from '../../../../../common/http_api/infra_ml'; +import { useTrackedPromise } from '../../../../utils/use_tracked_promise'; +import { npStart } from '../../../../legacy_singletons'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; + +export type SortOptions = Sort; +export type PaginationOptions = Pick; +export type Page = number; +export type FetchNextPage = () => void; +export type FetchPreviousPage = () => void; +export type ChangeSortOptions = (sortOptions: Sort) => void; +export type ChangePaginationOptions = (paginationOptions: PaginationOptions) => void; +export type MetricsHostsAnomalies = MetricsHostsAnomaly[]; +interface PaginationCursors { + previousPageCursor: PaginationCursor; + nextPageCursor: PaginationCursor; +} + +interface ReducerState { + page: number; + lastReceivedCursors: PaginationCursors | undefined; + paginationCursor: Pagination['cursor'] | undefined; + hasNextPage: boolean; + paginationOptions: PaginationOptions; + sortOptions: Sort; + timeRange: { + start: number; + end: number; + }; + filteredDatasets?: string[]; +} + +type ReducerStateDefaults = Pick< + ReducerState, + 'page' | 'lastReceivedCursors' | 'paginationCursor' | 'hasNextPage' +>; + +type ReducerAction = + | { type: 'changePaginationOptions'; payload: { paginationOptions: PaginationOptions } } + | { type: 'changeSortOptions'; payload: { sortOptions: Sort } } + | { type: 'fetchNextPage' } + | { type: 'fetchPreviousPage' } + | { type: 'changeHasNextPage'; payload: { hasNextPage: boolean } } + | { type: 'changeLastReceivedCursors'; payload: { lastReceivedCursors: PaginationCursors } } + | { type: 'changeTimeRange'; payload: { timeRange: { start: number; end: number } } } + | { type: 'changeFilteredDatasets'; payload: { filteredDatasets?: string[] } }; + +const stateReducer = (state: ReducerState, action: ReducerAction): ReducerState => { + const resetPagination = { + page: 1, + paginationCursor: undefined, + }; + switch (action.type) { + case 'changePaginationOptions': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + case 'changeSortOptions': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + case 'changeHasNextPage': + return { + ...state, + ...action.payload, + }; + case 'changeLastReceivedCursors': + return { + ...state, + ...action.payload, + }; + case 'fetchNextPage': + return state.lastReceivedCursors + ? { + ...state, + page: state.page + 1, + paginationCursor: { searchAfter: state.lastReceivedCursors.nextPageCursor }, + } + : state; + case 'fetchPreviousPage': + return state.lastReceivedCursors + ? { + ...state, + page: state.page - 1, + paginationCursor: { searchBefore: state.lastReceivedCursors.previousPageCursor }, + } + : state; + case 'changeTimeRange': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + case 'changeFilteredDatasets': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + default: + return state; + } +}; + +const STATE_DEFAULTS: ReducerStateDefaults = { + // NOTE: This piece of state is purely for the client side, it could be extracted out of the hook. + page: 1, + // Cursor from the last request + lastReceivedCursors: undefined, + // Cursor to use for the next request. For the first request, and therefore not paging, this will be undefined. + paginationCursor: undefined, + hasNextPage: false, +}; + +export const useMetricsHostsAnomaliesResults = ({ + endTime, + startTime, + sourceId, + defaultSortOptions, + defaultPaginationOptions, + onGetMetricsHostsAnomaliesDatasetsError, + filteredDatasets, +}: { + endTime: number; + startTime: number; + sourceId: string; + defaultSortOptions: Sort; + defaultPaginationOptions: Pick; + onGetMetricsHostsAnomaliesDatasetsError?: (error: Error) => void; + filteredDatasets?: string[]; +}) => { + const initStateReducer = (stateDefaults: ReducerStateDefaults): ReducerState => { + return { + ...stateDefaults, + paginationOptions: defaultPaginationOptions, + sortOptions: defaultSortOptions, + filteredDatasets, + timeRange: { + start: startTime, + end: endTime, + }, + }; + }; + + const [reducerState, dispatch] = useReducer(stateReducer, STATE_DEFAULTS, initStateReducer); + + const [metricsHostsAnomalies, setMetricsHostsAnomalies] = useState([]); + + const [getMetricsHostsAnomaliesRequest, getMetricsHostsAnomalies] = useTrackedPromise( + { + cancelPreviousOn: 'creation', + createPromise: async () => { + const { + timeRange: { start: queryStartTime, end: queryEndTime }, + sortOptions, + paginationOptions, + paginationCursor, + } = reducerState; + return await callGetMetricHostsAnomaliesAPI( + sourceId, + queryStartTime, + queryEndTime, + sortOptions, + { + ...paginationOptions, + cursor: paginationCursor, + } + ); + }, + onResolve: ({ data: { anomalies, paginationCursors: requestCursors, hasMoreEntries } }) => { + const { paginationCursor } = reducerState; + if (requestCursors) { + dispatch({ + type: 'changeLastReceivedCursors', + payload: { lastReceivedCursors: requestCursors }, + }); + } + // Check if we have more "next" entries. "Page" covers the "previous" scenario, + // since we need to know the page we're on anyway. + if (!paginationCursor || (paginationCursor && 'searchAfter' in paginationCursor)) { + dispatch({ type: 'changeHasNextPage', payload: { hasNextPage: hasMoreEntries } }); + } else if (paginationCursor && 'searchBefore' in paginationCursor) { + // We've requested a previous page, therefore there is a next page. + dispatch({ type: 'changeHasNextPage', payload: { hasNextPage: true } }); + } + setMetricsHostsAnomalies(anomalies); + }, + }, + [ + sourceId, + dispatch, + reducerState.timeRange, + reducerState.sortOptions, + reducerState.paginationOptions, + reducerState.paginationCursor, + reducerState.filteredDatasets, + ] + ); + + const changeSortOptions = useCallback( + (nextSortOptions: Sort) => { + dispatch({ type: 'changeSortOptions', payload: { sortOptions: nextSortOptions } }); + }, + [dispatch] + ); + + const changePaginationOptions = useCallback( + (nextPaginationOptions: PaginationOptions) => { + dispatch({ + type: 'changePaginationOptions', + payload: { paginationOptions: nextPaginationOptions }, + }); + }, + [dispatch] + ); + + // Time range has changed + useEffect(() => { + dispatch({ + type: 'changeTimeRange', + payload: { timeRange: { start: startTime, end: endTime } }, + }); + }, [startTime, endTime]); + + // Selected datasets have changed + useEffect(() => { + dispatch({ + type: 'changeFilteredDatasets', + payload: { filteredDatasets }, + }); + }, [filteredDatasets]); + + useEffect(() => { + getMetricsHostsAnomalies(); + }, [getMetricsHostsAnomalies]); // TODO: FIgure out the deps here. + + const handleFetchNextPage = useCallback(() => { + if (reducerState.lastReceivedCursors) { + dispatch({ type: 'fetchNextPage' }); + } + }, [dispatch, reducerState]); + + const handleFetchPreviousPage = useCallback(() => { + if (reducerState.lastReceivedCursors) { + dispatch({ type: 'fetchPreviousPage' }); + } + }, [dispatch, reducerState]); + + const isLoadingMetricsHostsAnomalies = useMemo( + () => getMetricsHostsAnomaliesRequest.state === 'pending', + [getMetricsHostsAnomaliesRequest.state] + ); + + const hasFailedLoadingMetricsHostsAnomalies = useMemo( + () => getMetricsHostsAnomaliesRequest.state === 'rejected', + [getMetricsHostsAnomaliesRequest.state] + ); + + return { + metricsHostsAnomalies, + getMetricsHostsAnomalies, + isLoadingMetricsHostsAnomalies, + hasFailedLoadingMetricsHostsAnomalies, + changeSortOptions, + sortOptions: reducerState.sortOptions, + changePaginationOptions, + paginationOptions: reducerState.paginationOptions, + fetchPreviousPage: reducerState.page > 1 ? handleFetchPreviousPage : undefined, + fetchNextPage: reducerState.hasNextPage ? handleFetchNextPage : undefined, + page: reducerState.page, + }; +}; + +export const callGetMetricHostsAnomaliesAPI = async ( + sourceId: string, + startTime: number, + endTime: number, + sort: Sort, + pagination: Pagination +) => { + const response = await npStart.http.fetch(INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH, { + method: 'POST', + body: JSON.stringify( + getMetricsHostsAnomaliesRequestPayloadRT.encode({ + data: { + sourceId, + timeRange: { + startTime, + endTime, + }, + sort, + pagination, + }, + }) + ), + }); + + return decodeOrThrow(getMetricsHostsAnomaliesSuccessReponsePayloadRT)(response); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_k8s_anomalies.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_k8s_anomalies.ts new file mode 100644 index 00000000000000..4a7b78e1fdf921 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_k8s_anomalies.ts @@ -0,0 +1,322 @@ +/* + * 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 { useMemo, useState, useCallback, useEffect, useReducer } from 'react'; +import { + Sort, + Pagination, + PaginationCursor, + INFA_ML_GET_METRICS_K8S_ANOMALIES_PATH, + getMetricsK8sAnomaliesSuccessReponsePayloadRT, + getMetricsK8sAnomaliesRequestPayloadRT, + MetricsK8sAnomaly, +} from '../../../../../common/http_api/infra_ml'; +import { useTrackedPromise } from '../../../../utils/use_tracked_promise'; +import { npStart } from '../../../../legacy_singletons'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; + +export type SortOptions = Sort; +export type PaginationOptions = Pick; +export type Page = number; +export type FetchNextPage = () => void; +export type FetchPreviousPage = () => void; +export type ChangeSortOptions = (sortOptions: Sort) => void; +export type ChangePaginationOptions = (paginationOptions: PaginationOptions) => void; +export type MetricsK8sAnomalies = MetricsK8sAnomaly[]; +interface PaginationCursors { + previousPageCursor: PaginationCursor; + nextPageCursor: PaginationCursor; +} + +interface ReducerState { + page: number; + lastReceivedCursors: PaginationCursors | undefined; + paginationCursor: Pagination['cursor'] | undefined; + hasNextPage: boolean; + paginationOptions: PaginationOptions; + sortOptions: Sort; + timeRange: { + start: number; + end: number; + }; + filteredDatasets?: string[]; +} + +type ReducerStateDefaults = Pick< + ReducerState, + 'page' | 'lastReceivedCursors' | 'paginationCursor' | 'hasNextPage' +>; + +type ReducerAction = + | { type: 'changePaginationOptions'; payload: { paginationOptions: PaginationOptions } } + | { type: 'changeSortOptions'; payload: { sortOptions: Sort } } + | { type: 'fetchNextPage' } + | { type: 'fetchPreviousPage' } + | { type: 'changeHasNextPage'; payload: { hasNextPage: boolean } } + | { type: 'changeLastReceivedCursors'; payload: { lastReceivedCursors: PaginationCursors } } + | { type: 'changeTimeRange'; payload: { timeRange: { start: number; end: number } } } + | { type: 'changeFilteredDatasets'; payload: { filteredDatasets?: string[] } }; + +const stateReducer = (state: ReducerState, action: ReducerAction): ReducerState => { + const resetPagination = { + page: 1, + paginationCursor: undefined, + }; + switch (action.type) { + case 'changePaginationOptions': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + case 'changeSortOptions': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + case 'changeHasNextPage': + return { + ...state, + ...action.payload, + }; + case 'changeLastReceivedCursors': + return { + ...state, + ...action.payload, + }; + case 'fetchNextPage': + return state.lastReceivedCursors + ? { + ...state, + page: state.page + 1, + paginationCursor: { searchAfter: state.lastReceivedCursors.nextPageCursor }, + } + : state; + case 'fetchPreviousPage': + return state.lastReceivedCursors + ? { + ...state, + page: state.page - 1, + paginationCursor: { searchBefore: state.lastReceivedCursors.previousPageCursor }, + } + : state; + case 'changeTimeRange': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + case 'changeFilteredDatasets': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + default: + return state; + } +}; + +const STATE_DEFAULTS: ReducerStateDefaults = { + // NOTE: This piece of state is purely for the client side, it could be extracted out of the hook. + page: 1, + // Cursor from the last request + lastReceivedCursors: undefined, + // Cursor to use for the next request. For the first request, and therefore not paging, this will be undefined. + paginationCursor: undefined, + hasNextPage: false, +}; + +export const useMetricsK8sAnomaliesResults = ({ + endTime, + startTime, + sourceId, + defaultSortOptions, + defaultPaginationOptions, + onGetMetricsHostsAnomaliesDatasetsError, + filteredDatasets, +}: { + endTime: number; + startTime: number; + sourceId: string; + defaultSortOptions: Sort; + defaultPaginationOptions: Pick; + onGetMetricsHostsAnomaliesDatasetsError?: (error: Error) => void; + filteredDatasets?: string[]; +}) => { + const initStateReducer = (stateDefaults: ReducerStateDefaults): ReducerState => { + return { + ...stateDefaults, + paginationOptions: defaultPaginationOptions, + sortOptions: defaultSortOptions, + filteredDatasets, + timeRange: { + start: startTime, + end: endTime, + }, + }; + }; + + const [reducerState, dispatch] = useReducer(stateReducer, STATE_DEFAULTS, initStateReducer); + + const [metricsK8sAnomalies, setMetricsK8sAnomalies] = useState([]); + + const [getMetricsK8sAnomaliesRequest, getMetricsK8sAnomalies] = useTrackedPromise( + { + cancelPreviousOn: 'creation', + createPromise: async () => { + const { + timeRange: { start: queryStartTime, end: queryEndTime }, + sortOptions, + paginationOptions, + paginationCursor, + filteredDatasets: queryFilteredDatasets, + } = reducerState; + return await callGetMetricsK8sAnomaliesAPI( + sourceId, + queryStartTime, + queryEndTime, + sortOptions, + { + ...paginationOptions, + cursor: paginationCursor, + }, + queryFilteredDatasets + ); + }, + onResolve: ({ data: { anomalies, paginationCursors: requestCursors, hasMoreEntries } }) => { + const { paginationCursor } = reducerState; + if (requestCursors) { + dispatch({ + type: 'changeLastReceivedCursors', + payload: { lastReceivedCursors: requestCursors }, + }); + } + // Check if we have more "next" entries. "Page" covers the "previous" scenario, + // since we need to know the page we're on anyway. + if (!paginationCursor || (paginationCursor && 'searchAfter' in paginationCursor)) { + dispatch({ type: 'changeHasNextPage', payload: { hasNextPage: hasMoreEntries } }); + } else if (paginationCursor && 'searchBefore' in paginationCursor) { + // We've requested a previous page, therefore there is a next page. + dispatch({ type: 'changeHasNextPage', payload: { hasNextPage: true } }); + } + setMetricsK8sAnomalies(anomalies); + }, + }, + [ + sourceId, + dispatch, + reducerState.timeRange, + reducerState.sortOptions, + reducerState.paginationOptions, + reducerState.paginationCursor, + reducerState.filteredDatasets, + ] + ); + + const changeSortOptions = useCallback( + (nextSortOptions: Sort) => { + dispatch({ type: 'changeSortOptions', payload: { sortOptions: nextSortOptions } }); + }, + [dispatch] + ); + + const changePaginationOptions = useCallback( + (nextPaginationOptions: PaginationOptions) => { + dispatch({ + type: 'changePaginationOptions', + payload: { paginationOptions: nextPaginationOptions }, + }); + }, + [dispatch] + ); + + // Time range has changed + useEffect(() => { + dispatch({ + type: 'changeTimeRange', + payload: { timeRange: { start: startTime, end: endTime } }, + }); + }, [startTime, endTime]); + + // Selected datasets have changed + useEffect(() => { + dispatch({ + type: 'changeFilteredDatasets', + payload: { filteredDatasets }, + }); + }, [filteredDatasets]); + + useEffect(() => { + getMetricsK8sAnomalies(); + }, [getMetricsK8sAnomalies]); + + const handleFetchNextPage = useCallback(() => { + if (reducerState.lastReceivedCursors) { + dispatch({ type: 'fetchNextPage' }); + } + }, [dispatch, reducerState]); + + const handleFetchPreviousPage = useCallback(() => { + if (reducerState.lastReceivedCursors) { + dispatch({ type: 'fetchPreviousPage' }); + } + }, [dispatch, reducerState]); + + const isLoadingMetricsK8sAnomalies = useMemo( + () => getMetricsK8sAnomaliesRequest.state === 'pending', + [getMetricsK8sAnomaliesRequest.state] + ); + + const hasFailedLoadingMetricsK8sAnomalies = useMemo( + () => getMetricsK8sAnomaliesRequest.state === 'rejected', + [getMetricsK8sAnomaliesRequest.state] + ); + + return { + metricsK8sAnomalies, + getMetricsK8sAnomalies, + isLoadingMetricsK8sAnomalies, + hasFailedLoadingMetricsK8sAnomalies, + changeSortOptions, + sortOptions: reducerState.sortOptions, + changePaginationOptions, + paginationOptions: reducerState.paginationOptions, + fetchPreviousPage: reducerState.page > 1 ? handleFetchPreviousPage : undefined, + fetchNextPage: reducerState.hasNextPage ? handleFetchNextPage : undefined, + page: reducerState.page, + }; +}; + +export const callGetMetricsK8sAnomaliesAPI = async ( + sourceId: string, + startTime: number, + endTime: number, + sort: Sort, + pagination: Pagination, + datasets?: string[] +) => { + const response = await npStart.http.fetch(INFA_ML_GET_METRICS_K8S_ANOMALIES_PATH, { + method: 'POST', + body: JSON.stringify( + getMetricsK8sAnomaliesRequestPayloadRT.encode({ + data: { + sourceId, + timeRange: { + startTime, + endTime, + }, + sort, + pagination, + datasets, + }, + }) + ), + }); + + return decodeOrThrow(getMetricsK8sAnomaliesSuccessReponsePayloadRT)(response); +}; diff --git a/x-pack/plugins/infra/server/infra_server.ts b/x-pack/plugins/infra/server/infra_server.ts index a72e40e25b4794..206fffdd2e1888 100644 --- a/x-pack/plugins/infra/server/infra_server.ts +++ b/x-pack/plugins/infra/server/infra_server.ts @@ -21,6 +21,8 @@ import { initGetLogEntryAnomaliesRoute, initGetLogEntryAnomaliesDatasetsRoute, } from './routes/log_analysis'; +import { initGetK8sAnomaliesRoute } from './routes/infra_ml'; +import { initGetHostsAnomaliesRoute } from './routes/infra_ml'; import { initMetricExplorerRoute } from './routes/metrics_explorer'; import { initMetadataRoute } from './routes/metadata'; import { initSnapshotRoute } from './routes/snapshot'; @@ -56,6 +58,8 @@ export const initInfraServer = (libs: InfraBackendLibs) => { initGetLogEntryRateRoute(libs); initGetLogEntryAnomaliesRoute(libs); initGetLogEntryAnomaliesDatasetsRoute(libs); + initGetK8sAnomaliesRoute(libs); + initGetHostsAnomaliesRoute(libs); initSnapshotRoute(libs); initNodeDetailsRoute(libs); initSourceRoute(libs); diff --git a/x-pack/plugins/infra/server/lib/infra_ml/common.ts b/x-pack/plugins/infra/server/lib/infra_ml/common.ts new file mode 100644 index 00000000000000..4d2be94c7cd628 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/common.ts @@ -0,0 +1,89 @@ +/* + * 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 type { MlAnomalyDetectors, MlSystem } from '../../types'; +import { NoLogAnalysisMlJobError } from './errors'; + +import { + CompositeDatasetKey, + createLogEntryDatasetsQuery, + LogEntryDatasetBucket, + logEntryDatasetsResponseRT, +} from './queries/log_entry_data_sets'; +import { decodeOrThrow } from '../../../common/runtime_types'; +import { startTracingSpan, TracingSpan } from '../../../common/performance_tracing'; + +export async function fetchMlJob(mlAnomalyDetectors: MlAnomalyDetectors, jobId: string) { + const finalizeMlGetJobSpan = startTracingSpan('Fetch ml job from ES'); + const { + jobs: [mlJob], + } = await mlAnomalyDetectors.jobs(jobId); + + const mlGetJobSpan = finalizeMlGetJobSpan(); + + if (mlJob == null) { + throw new NoLogAnalysisMlJobError(`Failed to find ml job ${jobId}.`); + } + + return { + mlJob, + timing: { + spans: [mlGetJobSpan], + }, + }; +} + +const COMPOSITE_AGGREGATION_BATCH_SIZE = 1000; + +// Finds datasets related to ML job ids +export async function getLogEntryDatasets( + mlSystem: MlSystem, + startTime: number, + endTime: number, + jobIds: string[] +) { + const finalizeLogEntryDatasetsSpan = startTracingSpan('get data sets'); + + let logEntryDatasetBuckets: LogEntryDatasetBucket[] = []; + let afterLatestBatchKey: CompositeDatasetKey | undefined; + let esSearchSpans: TracingSpan[] = []; + + while (true) { + const finalizeEsSearchSpan = startTracingSpan('fetch log entry dataset batch from ES'); + + const logEntryDatasetsResponse = decodeOrThrow(logEntryDatasetsResponseRT)( + await mlSystem.mlAnomalySearch( + createLogEntryDatasetsQuery( + jobIds, + startTime, + endTime, + COMPOSITE_AGGREGATION_BATCH_SIZE, + afterLatestBatchKey + ) + ) + ); + + const { after_key: afterKey, buckets: latestBatchBuckets = [] } = + logEntryDatasetsResponse.aggregations?.dataset_buckets ?? {}; + + logEntryDatasetBuckets = [...logEntryDatasetBuckets, ...latestBatchBuckets]; + afterLatestBatchKey = afterKey; + esSearchSpans = [...esSearchSpans, finalizeEsSearchSpan()]; + + if (latestBatchBuckets.length < COMPOSITE_AGGREGATION_BATCH_SIZE) { + break; + } + } + + const logEntryDatasetsSpan = finalizeLogEntryDatasetsSpan(); + + return { + data: logEntryDatasetBuckets.map((logEntryDatasetBucket) => logEntryDatasetBucket.key.dataset), + timing: { + spans: [logEntryDatasetsSpan, ...esSearchSpans], + }, + }; +} diff --git a/x-pack/plugins/infra/server/lib/infra_ml/errors.ts b/x-pack/plugins/infra/server/lib/infra_ml/errors.ts new file mode 100644 index 00000000000000..ad46ebf7102663 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/errors.ts @@ -0,0 +1,49 @@ +/* + * 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. + */ + +/* eslint-disable max-classes-per-file */ + +import { + UnknownMLCapabilitiesError, + InsufficientMLCapabilities, + MLPrivilegesUninitialized, +} from '../../../../ml/server'; + +export class NoLogAnalysisMlJobError extends Error { + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export class InsufficientLogAnalysisMlJobConfigurationError extends Error { + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export class UnknownCategoryError extends Error { + constructor(categoryId: number) { + super(`Unknown ml category ${categoryId}`); + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export class InsufficientAnomalyMlJobsConfigured extends Error { + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export const isMlPrivilegesError = (error: any) => { + return ( + error instanceof UnknownMLCapabilitiesError || + error instanceof InsufficientMLCapabilities || + error instanceof MLPrivilegesUninitialized + ); +}; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/index.ts b/x-pack/plugins/infra/server/lib/infra_ml/index.ts new file mode 100644 index 00000000000000..536f0a44d58906 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/index.ts @@ -0,0 +1,9 @@ +/* + * 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 * from './errors'; +export * from './metrics_hosts_anomalies'; +export * from './metrics_k8s_anomalies'; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts new file mode 100644 index 00000000000000..e0afa458aac88f --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts @@ -0,0 +1,289 @@ +/* + * 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 { RequestHandlerContext } from 'src/core/server'; +import { InfraRequestHandlerContext } from '../../types'; +import { TracingSpan, startTracingSpan } from '../../../common/performance_tracing'; +import { fetchMlJob, getLogEntryDatasets } from './common'; +import { getJobId, metricsHostsJobTypes } from '../../../common/infra_ml'; +import { Sort, Pagination } from '../../../common/http_api/infra_ml'; +import type { MlSystem, MlAnomalyDetectors } from '../../types'; +import { InsufficientAnomalyMlJobsConfigured, isMlPrivilegesError } from './errors'; +import { decodeOrThrow } from '../../../common/runtime_types'; +import { + metricsHostsAnomaliesResponseRT, + createMetricsHostsAnomaliesQuery, +} from './queries/metrics_hosts_anomalies'; + +interface MappedAnomalyHit { + id: string; + anomalyScore: number; + dataset: string; + typical: number; + actual: number; + jobId: string; + startTime: number; + duration: number; + hostName: string[]; + categoryId?: string; +} + +async function getCompatibleAnomaliesJobIds( + spaceId: string, + sourceId: string, + mlAnomalyDetectors: MlAnomalyDetectors +) { + const metricsHostsJobIds = metricsHostsJobTypes.map((jt) => getJobId(spaceId, sourceId, jt)); + + const jobIds: string[] = []; + let jobSpans: TracingSpan[] = []; + + try { + await Promise.all( + metricsHostsJobIds.map((id) => { + return (async () => { + const { + timing: { spans }, + } = await fetchMlJob(mlAnomalyDetectors, id); + jobIds.push(id); + jobSpans = [...jobSpans, ...spans]; + })(); + }) + ); + } catch (e) { + if (isMlPrivilegesError(e)) { + throw e; + } + // An error is also thrown when no jobs are found + } + + return { + jobIds, + timing: { spans: jobSpans }, + }; +} + +export async function getMetricsHostsAnomalies( + context: RequestHandlerContext & { infra: Required }, + sourceId: string, + startTime: number, + endTime: number, + sort: Sort, + pagination: Pagination +) { + const finalizeMetricsHostsAnomaliesSpan = startTracingSpan('get metrics hosts entry anomalies'); + + const { + jobIds, + timing: { spans: jobSpans }, + } = await getCompatibleAnomaliesJobIds( + context.infra.spaceId, + sourceId, + context.infra.mlAnomalyDetectors + ); + + if (jobIds.length === 0) { + throw new InsufficientAnomalyMlJobsConfigured( + 'Metrics Hosts ML jobs need to be configured to search anomalies' + ); + } + + try { + const { + anomalies, + paginationCursors, + hasMoreEntries, + timing: { spans: fetchLogEntryAnomaliesSpans }, + } = await fetchMetricsHostsAnomalies( + context.infra.mlSystem, + jobIds, + startTime, + endTime, + sort, + pagination + ); + + const data = anomalies.map((anomaly) => { + const { jobId } = anomaly; + + return parseAnomalyResult(anomaly, jobId); + }); + + const metricsHostsAnomaliesSpan = finalizeMetricsHostsAnomaliesSpan(); + + return { + data, + paginationCursors, + hasMoreEntries, + timing: { + spans: [metricsHostsAnomaliesSpan, ...jobSpans, ...fetchLogEntryAnomaliesSpans], + }, + }; + } catch (e) { + throw new Error(e); + } +} + +const parseAnomalyResult = (anomaly: MappedAnomalyHit, jobId: string) => { + const { + id, + anomalyScore, + dataset, + typical, + actual, + duration, + hostName, + startTime: anomalyStartTime, + } = anomaly; + + return { + id, + anomalyScore, + dataset, + typical, + actual, + duration, + hostName, + startTime: anomalyStartTime, + type: 'metrics_hosts' as const, + jobId, + }; +}; + +async function fetchMetricsHostsAnomalies( + mlSystem: MlSystem, + jobIds: string[], + startTime: number, + endTime: number, + sort: Sort, + pagination: Pagination +) { + // We'll request 1 extra entry on top of our pageSize to determine if there are + // more entries to be fetched. This avoids scenarios where the client side can't + // determine if entries.length === pageSize actually means there are more entries / next page + // or not. + const expandedPagination = { ...pagination, pageSize: pagination.pageSize + 1 }; + + const finalizeFetchLogEntryAnomaliesSpan = startTracingSpan('fetch metrics hosts anomalies'); + + // console.log( + // 'data', + // JSON.stringify( + // await mlSystem.mlAnomalySearch( + // createMetricsHostsAnomaliesQuery(jobIds, startTime, endTime, sort, expandedPagination) + // ), + // null, + // 2 + // ) + // ); + const results = decodeOrThrow(metricsHostsAnomaliesResponseRT)( + await mlSystem.mlAnomalySearch( + createMetricsHostsAnomaliesQuery(jobIds, startTime, endTime, sort, expandedPagination) + ) + ); + + const { + hits: { hits }, + } = results; + const hasMoreEntries = hits.length > pagination.pageSize; + + // An extra entry was found and hasMoreEntries has been determined, the extra entry can be removed. + if (hasMoreEntries) { + hits.pop(); + } + + // To "search_before" the sort order will have been reversed for ES. + // The results are now reversed back, to match the requested sort. + if (pagination.cursor && 'searchBefore' in pagination.cursor) { + hits.reverse(); + } + + const paginationCursors = + hits.length > 0 + ? { + previousPageCursor: hits[0].sort, + nextPageCursor: hits[hits.length - 1].sort, + } + : undefined; + + const anomalies = hits.map((result) => { + const { + // eslint-disable-next-line @typescript-eslint/naming-convention + job_id, + record_score: anomalyScore, + typical, + actual, + bucket_span: duration, + timestamp: anomalyStartTime, + by_field_value: categoryId, + } = result._source; + + return { + id: result._id, + anomalyScore, + dataset: '', + typical: typical[0], + actual: actual[0], + jobId: job_id, + hostName: result._source['host.name'], + startTime: anomalyStartTime, + duration: duration * 1000, + categoryId, + }; + }); + + const fetchLogEntryAnomaliesSpan = finalizeFetchLogEntryAnomaliesSpan(); + + return { + anomalies, + paginationCursors, + hasMoreEntries, + timing: { + spans: [fetchLogEntryAnomaliesSpan], + }, + }; +} + +// TODO: FIgure out why we need datasets +export async function getMetricsHostsAnomaliesDatasets( + context: { + infra: { + mlSystem: MlSystem; + mlAnomalyDetectors: MlAnomalyDetectors; + spaceId: string; + }; + }, + sourceId: string, + startTime: number, + endTime: number +) { + const { + jobIds, + timing: { spans: jobSpans }, + } = await getCompatibleAnomaliesJobIds( + context.infra.spaceId, + sourceId, + context.infra.mlAnomalyDetectors + ); + + if (jobIds.length === 0) { + throw new InsufficientAnomalyMlJobsConfigured( + 'Log rate or categorisation ML jobs need to be configured to search for anomaly datasets' + ); + } + + const { + data: datasets, + timing: { spans: datasetsSpans }, + } = await getLogEntryDatasets(context.infra.mlSystem, startTime, endTime, jobIds); + + return { + datasets, + timing: { + spans: [...jobSpans, ...datasetsSpans], + }, + }; +} diff --git a/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts new file mode 100644 index 00000000000000..29507900e1847f --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts @@ -0,0 +1,272 @@ +/* + * 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 { RequestHandlerContext } from 'src/core/server'; +import { InfraRequestHandlerContext } from '../../types'; +import { TracingSpan, startTracingSpan } from '../../../common/performance_tracing'; +import { fetchMlJob, getLogEntryDatasets } from './common'; +import { getJobId, metricsK8SJobTypes } from '../../../common/infra_ml'; +import { Sort, Pagination } from '../../../common/http_api/infra_ml'; +import type { MlSystem, MlAnomalyDetectors } from '../../types'; +import { InsufficientAnomalyMlJobsConfigured, isMlPrivilegesError } from './errors'; +import { decodeOrThrow } from '../../../common/runtime_types'; +import { + metricsK8sAnomaliesResponseRT, + createMetricsK8sAnomaliesQuery, +} from './queries/metrics_k8s_anomalies'; + +interface MappedAnomalyHit { + id: string; + anomalyScore: number; + // dataset: string; + typical: number; + actual: number; + jobId: string; + startTime: number; + duration: number; + categoryId?: string; +} + +async function getCompatibleAnomaliesJobIds( + spaceId: string, + sourceId: string, + mlAnomalyDetectors: MlAnomalyDetectors +) { + const metricsK8sJobIds = metricsK8SJobTypes.map((jt) => getJobId(spaceId, sourceId, jt)); + + const jobIds: string[] = []; + let jobSpans: TracingSpan[] = []; + + try { + await Promise.all( + metricsK8sJobIds.map((id) => { + return (async () => { + const { + timing: { spans }, + } = await fetchMlJob(mlAnomalyDetectors, id); + jobIds.push(id); + jobSpans = [...jobSpans, ...spans]; + })(); + }) + ); + } catch (e) { + if (isMlPrivilegesError(e)) { + throw e; + } + // An error is also thrown when no jobs are found + } + + return { + jobIds, + timing: { spans: jobSpans }, + }; +} + +export async function getMetricK8sAnomalies( + context: RequestHandlerContext & { infra: Required }, + sourceId: string, + startTime: number, + endTime: number, + sort: Sort, + pagination: Pagination +) { + const finalizeMetricsK8sAnomaliesSpan = startTracingSpan('get metrics k8s entry anomalies'); + + const { + jobIds, + timing: { spans: jobSpans }, + } = await getCompatibleAnomaliesJobIds( + context.infra.spaceId, + sourceId, + context.infra.mlAnomalyDetectors + ); + + if (jobIds.length === 0) { + throw new InsufficientAnomalyMlJobsConfigured( + 'Log rate or categorisation ML jobs need to be configured to search anomalies' + ); + } + + const { + anomalies, + paginationCursors, + hasMoreEntries, + timing: { spans: fetchLogEntryAnomaliesSpans }, + } = await fetchMetricK8sAnomalies( + context.infra.mlSystem, + jobIds, + startTime, + endTime, + sort, + pagination + ); + + const data = anomalies.map((anomaly) => { + const { jobId } = anomaly; + + return parseAnomalyResult(anomaly, jobId); + }); + + const metricsK8sAnomaliesSpan = finalizeMetricsK8sAnomaliesSpan(); + + return { + data, + paginationCursors, + hasMoreEntries, + timing: { + spans: [metricsK8sAnomaliesSpan, ...jobSpans, ...fetchLogEntryAnomaliesSpans], + }, + }; +} + +const parseAnomalyResult = (anomaly: MappedAnomalyHit, jobId: string) => { + const { + id, + anomalyScore, + // dataset, + typical, + actual, + duration, + startTime: anomalyStartTime, + } = anomaly; + + return { + id, + anomalyScore, + // dataset, + typical, + actual, + duration, + startTime: anomalyStartTime, + type: 'metrics_k8s' as const, + jobId, + }; +}; + +async function fetchMetricK8sAnomalies( + mlSystem: MlSystem, + jobIds: string[], + startTime: number, + endTime: number, + sort: Sort, + pagination: Pagination +) { + // We'll request 1 extra entry on top of our pageSize to determine if there are + // more entries to be fetched. This avoids scenarios where the client side can't + // determine if entries.length === pageSize actually means there are more entries / next page + // or not. + const expandedPagination = { ...pagination, pageSize: pagination.pageSize + 1 }; + + const finalizeFetchLogEntryAnomaliesSpan = startTracingSpan('fetch metrics k8s anomalies'); + + const results = decodeOrThrow(metricsK8sAnomaliesResponseRT)( + await mlSystem.mlAnomalySearch( + createMetricsK8sAnomaliesQuery(jobIds, startTime, endTime, sort, expandedPagination) + ) + ); + + const { + hits: { hits }, + } = results; + const hasMoreEntries = hits.length > pagination.pageSize; + + // An extra entry was found and hasMoreEntries has been determined, the extra entry can be removed. + if (hasMoreEntries) { + hits.pop(); + } + + // To "search_before" the sort order will have been reversed for ES. + // The results are now reversed back, to match the requested sort. + if (pagination.cursor && 'searchBefore' in pagination.cursor) { + hits.reverse(); + } + + const paginationCursors = + hits.length > 0 + ? { + previousPageCursor: hits[0].sort, + nextPageCursor: hits[hits.length - 1].sort, + } + : undefined; + + const anomalies = hits.map((result) => { + const { + // eslint-disable-next-line @typescript-eslint/naming-convention + job_id, + record_score: anomalyScore, + typical, + actual, + // partition_field_value: dataset, + bucket_span: duration, + timestamp: anomalyStartTime, + by_field_value: categoryId, + } = result._source; + + return { + id: result._id, + anomalyScore, + // dataset, + typical: typical[0], + actual: actual[0], + jobId: job_id, + startTime: anomalyStartTime, + duration: duration * 1000, + categoryId, + }; + }); + + const fetchLogEntryAnomaliesSpan = finalizeFetchLogEntryAnomaliesSpan(); + + return { + anomalies, + paginationCursors, + hasMoreEntries, + timing: { + spans: [fetchLogEntryAnomaliesSpan], + }, + }; +} + +// TODO: FIgure out why we need datasets +export async function getMetricK8sAnomaliesDatasets( + context: { + infra: { + mlSystem: MlSystem; + mlAnomalyDetectors: MlAnomalyDetectors; + spaceId: string; + }; + }, + sourceId: string, + startTime: number, + endTime: number +) { + const { + jobIds, + timing: { spans: jobSpans }, + } = await getCompatibleAnomaliesJobIds( + context.infra.spaceId, + sourceId, + context.infra.mlAnomalyDetectors + ); + + if (jobIds.length === 0) { + throw new InsufficientAnomalyMlJobsConfigured( + 'Log rate or categorisation ML jobs need to be configured to search for anomaly datasets' + ); + } + + const { + data: datasets, + timing: { spans: datasetsSpans }, + } = await getLogEntryDatasets(context.infra.mlSystem, startTime, endTime, jobIds); + + return { + datasets, + timing: { + spans: [...jobSpans, ...datasetsSpans], + }, + }; +} diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/common.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/common.ts new file mode 100644 index 00000000000000..63e39ef022392a --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/common.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const defaultRequestParameters = { + allowNoIndices: true, + ignoreUnavailable: true, + trackScores: false, + trackTotalHits: false, +}; + +export const createJobIdFilters = (jobId: string) => [ + { + term: { + job_id: { + value: jobId, + }, + }, + }, +]; + +export const createJobIdsFilters = (jobIds: string[]) => [ + { + terms: { + job_id: jobIds, + }, + }, +]; + +export const createTimeRangeFilters = (startTime: number, endTime: number) => [ + { + range: { + timestamp: { + gte: startTime, + lte: endTime, + }, + }, + }, +]; + +export const createResultTypeFilters = (resultTypes: Array<'model_plot' | 'record'>) => [ + { + terms: { + result_type: resultTypes, + }, + }, +]; + +export const createCategoryIdFilters = (categoryIds: number[]) => [ + { + terms: { + category_id: categoryIds, + }, + }, +]; + +export const createDatasetsFilters = (datasets?: string[]) => + datasets && datasets.length > 0 + ? [ + { + terms: { + partition_field_value: datasets, + }, + }, + ] + : []; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/index.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/index.ts new file mode 100644 index 00000000000000..5a42011e1cea12 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/index.ts @@ -0,0 +1,7 @@ +/* + * 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 * from './metrics_k8s_anomalies'; +export * from './metrics_hosts_anomalies'; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/log_entry_data_sets.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/log_entry_data_sets.ts new file mode 100644 index 00000000000000..53971a91d86b14 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/log_entry_data_sets.ts @@ -0,0 +1,84 @@ +/* + * 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 * as rt from 'io-ts'; +import { commonSearchSuccessResponseFieldsRT } from '../../../utils/elasticsearch_runtime_types'; +import { + createJobIdsFilters, + createResultTypeFilters, + createTimeRangeFilters, + defaultRequestParameters, +} from './common'; + +export const createLogEntryDatasetsQuery = ( + jobIds: string[], + startTime: number, + endTime: number, + size: number, + afterKey?: CompositeDatasetKey +) => ({ + ...defaultRequestParameters, + body: { + query: { + bool: { + filter: [ + ...createJobIdsFilters(jobIds), + ...createTimeRangeFilters(startTime, endTime), + ...createResultTypeFilters(['model_plot']), + ], + }, + }, + aggs: { + dataset_buckets: { + composite: { + after: afterKey, + size, + sources: [ + { + dataset: { + terms: { + field: 'partition_field_value', + order: 'asc', + }, + }, + }, + ], + }, + }, + }, + }, + size: 0, +}); + +const compositeDatasetKeyRT = rt.type({ + dataset: rt.string, +}); + +export type CompositeDatasetKey = rt.TypeOf; + +const logEntryDatasetBucketRT = rt.type({ + key: compositeDatasetKeyRT, +}); + +export type LogEntryDatasetBucket = rt.TypeOf; + +export const logEntryDatasetsResponseRT = rt.intersection([ + commonSearchSuccessResponseFieldsRT, + rt.partial({ + aggregations: rt.type({ + dataset_buckets: rt.intersection([ + rt.type({ + buckets: rt.array(logEntryDatasetBucketRT), + }), + rt.partial({ + after_key: compositeDatasetKeyRT, + }), + ]), + }), + }), +]); + +export type LogEntryDatasetsResponse = rt.TypeOf; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_hosts_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_hosts_anomalies.ts new file mode 100644 index 00000000000000..b61119b60bc184 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_hosts_anomalies.ts @@ -0,0 +1,131 @@ +/* + * 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 * as rt from 'io-ts'; +import { commonSearchSuccessResponseFieldsRT } from '../../../utils/elasticsearch_runtime_types'; +import { + createJobIdsFilters, + createTimeRangeFilters, + createResultTypeFilters, + defaultRequestParameters, +} from './common'; +import { Sort, Pagination } from '../../../../common/http_api/infra_ml'; + +// TODO: Reassess validity of this against ML docs +const TIEBREAKER_FIELD = '_doc'; + +const sortToMlFieldMap = { + dataset: 'partition_field_value', + anomalyScore: 'record_score', + startTime: 'timestamp', +}; + +export const createMetricsHostsAnomaliesQuery = ( + jobIds: string[], + startTime: number, + endTime: number, + sort: Sort, + pagination: Pagination +) => { + const { field } = sort; + const { pageSize } = pagination; + + const filters = [ + ...createJobIdsFilters(jobIds), + ...createTimeRangeFilters(startTime, endTime), + ...createResultTypeFilters(['record']), + ]; + + const sourceFields = [ + 'job_id', + 'record_score', + 'typical', + 'actual', + 'partition_field_value', + 'timestamp', + 'bucket_span', + 'by_field_value', + 'host.name', + 'influencers.influencer_field_name', + 'influencers.influencer_field_values', + ]; + + const { querySortDirection, queryCursor } = parsePaginationCursor(sort, pagination); + + const sortOptions = [ + { [sortToMlFieldMap[field]]: querySortDirection }, + { [TIEBREAKER_FIELD]: querySortDirection }, // Tiebreaker + ]; + + const resultsQuery = { + ...defaultRequestParameters, + body: { + query: { + bool: { + filter: filters, + }, + }, + search_after: queryCursor, + sort: sortOptions, + size: pageSize, + _source: sourceFields, + }, + }; + + return resultsQuery; +}; + +export const metricsHostsAnomalyHitRT = rt.type({ + _id: rt.string, + _source: rt.intersection([ + rt.type({ + job_id: rt.string, + record_score: rt.number, + typical: rt.array(rt.number), + actual: rt.array(rt.number), + 'host.name': rt.array(rt.string), + bucket_span: rt.number, + timestamp: rt.number, + }), + rt.partial({ + by_field_value: rt.string, + }), + ]), + sort: rt.tuple([rt.union([rt.string, rt.number]), rt.union([rt.string, rt.number])]), +}); + +export type MetricsHostsAnomalyHit = rt.TypeOf; + +export const metricsHostsAnomaliesResponseRT = rt.intersection([ + commonSearchSuccessResponseFieldsRT, + rt.type({ + hits: rt.type({ + hits: rt.array(metricsHostsAnomalyHitRT), + }), + }), +]); + +export type MetricsHostsAnomaliesResponseRT = rt.TypeOf; + +const parsePaginationCursor = (sort: Sort, pagination: Pagination) => { + const { cursor } = pagination; + const { direction } = sort; + + if (!cursor) { + return { querySortDirection: direction, queryCursor: undefined }; + } + + // We will always use ES's search_after to paginate, to mimic "search_before" behaviour we + // need to reverse the user's chosen search direction for the ES query. + if ('searchBefore' in cursor) { + return { + querySortDirection: direction === 'desc' ? 'asc' : 'desc', + queryCursor: cursor.searchBefore, + }; + } else { + return { querySortDirection: direction, queryCursor: cursor.searchAfter }; + } +}; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.ts new file mode 100644 index 00000000000000..84ed8b064c5ca8 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.ts @@ -0,0 +1,128 @@ +/* + * 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 * as rt from 'io-ts'; +import { commonSearchSuccessResponseFieldsRT } from '../../../utils/elasticsearch_runtime_types'; +import { + createJobIdsFilters, + createTimeRangeFilters, + createResultTypeFilters, + defaultRequestParameters, +} from './common'; +import { Sort, Pagination } from '../../../../common/http_api/infra_ml'; + +// TODO: Reassess validity of this against ML docs +const TIEBREAKER_FIELD = '_doc'; + +const sortToMlFieldMap = { + dataset: 'partition_field_value', + anomalyScore: 'record_score', + startTime: 'timestamp', +}; + +export const createMetricsK8sAnomaliesQuery = ( + jobIds: string[], + startTime: number, + endTime: number, + sort: Sort, + pagination: Pagination +) => { + const { field } = sort; + const { pageSize } = pagination; + + const filters = [ + ...createJobIdsFilters(jobIds), + ...createTimeRangeFilters(startTime, endTime), + ...createResultTypeFilters(['record']), + ]; + + const sourceFields = [ + 'job_id', + 'record_score', + 'typical', + 'actual', + 'partition_field_value', + 'timestamp', + 'bucket_span', + 'by_field_value', + ]; + + const { querySortDirection, queryCursor } = parsePaginationCursor(sort, pagination); + + const sortOptions = [ + { [sortToMlFieldMap[field]]: querySortDirection }, + { [TIEBREAKER_FIELD]: querySortDirection }, // Tiebreaker + ]; + + const resultsQuery = { + ...defaultRequestParameters, + body: { + query: { + bool: { + filter: filters, + }, + }, + search_after: queryCursor, + sort: sortOptions, + size: pageSize, + _source: sourceFields, + }, + }; + + return resultsQuery; +}; + +export const metricsK8sAnomalyHitRT = rt.type({ + _id: rt.string, + _source: rt.intersection([ + rt.type({ + job_id: rt.string, + record_score: rt.number, + typical: rt.array(rt.number), + actual: rt.array(rt.number), + // partition_field_value: rt.string, + bucket_span: rt.number, + timestamp: rt.number, + }), + rt.partial({ + by_field_value: rt.string, + }), + ]), + sort: rt.tuple([rt.union([rt.string, rt.number]), rt.union([rt.string, rt.number])]), +}); + +export type MetricsK8sAnomalyHit = rt.TypeOf; + +export const metricsK8sAnomaliesResponseRT = rt.intersection([ + commonSearchSuccessResponseFieldsRT, + rt.type({ + hits: rt.type({ + hits: rt.array(metricsK8sAnomalyHitRT), + }), + }), +]); + +export type MetricsK8sAnomaliesResponseRT = rt.TypeOf; + +const parsePaginationCursor = (sort: Sort, pagination: Pagination) => { + const { cursor } = pagination; + const { direction } = sort; + + if (!cursor) { + return { querySortDirection: direction, queryCursor: undefined }; + } + + // We will always use ES's search_after to paginate, to mimic "search_before" behaviour we + // need to reverse the user's chosen search direction for the ES query. + if ('searchBefore' in cursor) { + return { + querySortDirection: direction === 'desc' ? 'asc' : 'desc', + queryCursor: cursor.searchBefore, + }; + } else { + return { querySortDirection: direction, queryCursor: cursor.searchAfter }; + } +}; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/ml_jobs.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/ml_jobs.ts new file mode 100644 index 00000000000000..ee4ccbfaeb5a7d --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/ml_jobs.ts @@ -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 * as rt from 'io-ts'; + +export const createMlJobsQuery = (jobIds: string[]) => ({ + method: 'GET', + path: `/_ml/anomaly_detectors/${jobIds.join(',')}`, + query: { + allow_no_jobs: true, + }, +}); + +export const mlJobRT = rt.type({ + job_id: rt.string, + custom_settings: rt.unknown, +}); + +export const mlJobsResponseRT = rt.type({ + jobs: rt.array(mlJobRT), +}); diff --git a/x-pack/plugins/infra/server/routes/infra_ml/index.ts b/x-pack/plugins/infra/server/routes/infra_ml/index.ts new file mode 100644 index 00000000000000..38684cb22e237f --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra_ml/index.ts @@ -0,0 +1,7 @@ +/* + * 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 * from './results'; diff --git a/x-pack/plugins/infra/server/routes/infra_ml/results/index.ts b/x-pack/plugins/infra/server/routes/infra_ml/results/index.ts new file mode 100644 index 00000000000000..82e30291faa207 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra_ml/results/index.ts @@ -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 * from './metrics_hosts_anomalies'; +export * from './metrics_k8s_anomalies'; diff --git a/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_hosts_anomalies.ts b/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_hosts_anomalies.ts new file mode 100644 index 00000000000000..29122ae159cdc3 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_hosts_anomalies.ts @@ -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 Boom from 'boom'; +import { InfraBackendLibs } from '../../../lib/infra_types'; +import { + INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH, + getMetricsHostsAnomaliesSuccessReponsePayloadRT, + getMetricsHostsAnomaliesRequestPayloadRT, + GetMetricsHostsAnomaliesRequestPayload, + Sort, + Pagination, +} from '../../../../common/http_api/infra_ml'; +import { createValidationFunction } from '../../../../common/runtime_types'; +import { assertHasInfraMlPlugins } from '../../../utils/request_context'; + +import { isMlPrivilegesError } from '../../../lib/infra_ml/errors'; +import { getMetricsHostsAnomalies } from '../../../lib/infra_ml'; + +export const initGetHostsAnomaliesRoute = ({ framework }: InfraBackendLibs) => { + framework.registerRoute( + { + method: 'post', + path: INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH, + validate: { + body: createValidationFunction(getMetricsHostsAnomaliesRequestPayloadRT), + }, + }, + framework.router.handleLegacyErrors(async (requestContext, request, response) => { + const { + data: { + sourceId, + timeRange: { startTime, endTime }, + sort: sortParam, + pagination: paginationParam, + }, + } = request.body; + + const { sort, pagination } = getSortAndPagination(sortParam, paginationParam); + + try { + assertHasInfraMlPlugins(requestContext); + + const { + data: anomalies, + paginationCursors, + hasMoreEntries, + timing, + } = await getMetricsHostsAnomalies( + requestContext, + sourceId, + startTime, + endTime, + sort, + pagination + ); + + // console.log('---- anomalies', anomalies); + + return response.ok({ + body: getMetricsHostsAnomaliesSuccessReponsePayloadRT.encode({ + data: { + anomalies, + hasMoreEntries, + paginationCursors, + }, + timing, + }), + }); + } catch (error) { + if (Boom.isBoom(error)) { + throw error; + } + + if (isMlPrivilegesError(error)) { + return response.customError({ + statusCode: 403, + body: { + message: error.message, + }, + }); + } + + return response.customError({ + statusCode: error.statusCode ?? 500, + body: { + message: error.message ?? 'An unexpected error occurred', + }, + }); + } + }) + ); +}; + +const getSortAndPagination = ( + sort: Partial = {}, + pagination: Partial = {} +): { + sort: Sort; + pagination: Pagination; +} => { + const sortDefaults = { + field: 'anomalyScore' as const, + direction: 'desc' as const, + }; + + const sortWithDefaults = { + ...sortDefaults, + ...sort, + }; + + const paginationDefaults = { + pageSize: 50, + }; + + const paginationWithDefaults = { + ...paginationDefaults, + ...pagination, + }; + + return { sort: sortWithDefaults, pagination: paginationWithDefaults }; +}; diff --git a/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_k8s_anomalies.ts b/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_k8s_anomalies.ts new file mode 100644 index 00000000000000..5260c55836c59d --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_k8s_anomalies.ts @@ -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 Boom from 'boom'; +import { InfraBackendLibs } from '../../../lib/infra_types'; +import { + INFA_ML_GET_METRICS_K8S_ANOMALIES_PATH, + getMetricsK8sAnomaliesSuccessReponsePayloadRT, + getMetricsK8sAnomaliesRequestPayloadRT, + GetMetricsK8sAnomaliesRequestPayload, + Sort, + Pagination, +} from '../../../../common/http_api/infra_ml'; +import { createValidationFunction } from '../../../../common/runtime_types'; +import { assertHasInfraMlPlugins } from '../../../utils/request_context'; +import { getMetricK8sAnomalies } from '../../../lib/infra_ml'; +import { isMlPrivilegesError } from '../../../lib/infra_ml/errors'; + +export const initGetK8sAnomaliesRoute = ({ framework }: InfraBackendLibs) => { + framework.registerRoute( + { + method: 'post', + path: INFA_ML_GET_METRICS_K8S_ANOMALIES_PATH, + validate: { + body: createValidationFunction(getMetricsK8sAnomaliesRequestPayloadRT), + }, + }, + framework.router.handleLegacyErrors(async (requestContext, request, response) => { + const { + data: { + sourceId, + timeRange: { startTime, endTime }, + sort: sortParam, + pagination: paginationParam, + }, + } = request.body; + + const { sort, pagination } = getSortAndPagination(sortParam, paginationParam); + + try { + assertHasInfraMlPlugins(requestContext); + + const { + data: anomalies, + paginationCursors, + hasMoreEntries, + timing, + } = await getMetricK8sAnomalies( + requestContext, + sourceId, + startTime, + endTime, + sort, + pagination + ); + + return response.ok({ + body: getMetricsK8sAnomaliesSuccessReponsePayloadRT.encode({ + data: { + anomalies, + hasMoreEntries, + paginationCursors, + }, + timing, + }), + }); + } catch (error) { + if (Boom.isBoom(error)) { + throw error; + } + + if (isMlPrivilegesError(error)) { + return response.customError({ + statusCode: 403, + body: { + message: error.message, + }, + }); + } + + return response.customError({ + statusCode: error.statusCode ?? 500, + body: { + message: error.message ?? 'An unexpected error occurred', + }, + }); + } + }) + ); +}; + +const getSortAndPagination = ( + sort: Partial = {}, + pagination: Partial = {} +): { + sort: Sort; + pagination: Pagination; +} => { + const sortDefaults = { + field: 'anomalyScore' as const, + direction: 'desc' as const, + }; + + const sortWithDefaults = { + ...sortDefaults, + ...sort, + }; + + const paginationDefaults = { + pageSize: 50, + }; + + const paginationWithDefaults = { + ...paginationDefaults, + ...pagination, + }; + + return { sort: sortWithDefaults, pagination: paginationWithDefaults }; +}; From 0db3159a9fe960c6f15c2db5216bf7bc1502dff8 Mon Sep 17 00:00:00 2001 From: Constance Date: Wed, 23 Sep 2020 11:36:13 -0700 Subject: [PATCH 13/35] [Enterprise Search] Move LicenseContext to Kea (#78231) * Fix licensing to use start service + refactor - I noticed my IDE complaining that we were using LicensingPluginSetup (deprecated) instead of LicensingPluginStart, and decided to factor plugin.ts to DRY out / ensure all the dependencies we were passing on app mount were start services and not setup - The number of args we were passing to renderApp was getting a little ridiculous, so I created small helpers to group them up by type (Kibana's args (dependencies/services) vs our plugin's args (data, config, etc.) + bonus remove unused CoreStart type/arg * Add LicensingLogic + mount - replaces useObservable with a manual subscription that updates the license value/state - moves hasXLicense checks to selectors vs helper functions * Update components w/ license checks to use LicensingLogic * Update tests for components now calling LicensingLogic - Add mockLicensingValues to basic kea mock - Minor comment update to mockAllValues obj that I forgot to add in a previous PR * :fire: Remove old LicensingContext --- .../public/applications/__mocks__/index.ts | 2 +- .../public/applications/__mocks__/kea.mock.ts | 4 + ...ontext.mock.ts => licensing_logic.mock.ts} | 4 +- .../__mocks__/mount_with_context.mock.tsx | 6 +- .../__mocks__/shallow_usecontext.mock.ts | 3 +- .../engine_overview/engine_overview.test.tsx | 6 +- .../engine_overview/engine_overview.tsx | 10 +- .../public/applications/index.test.tsx | 30 ++-- .../public/applications/index.tsx | 30 ++-- .../applications/shared/licensing/index.ts | 3 +- .../shared/licensing/license_checks.test.ts | 54 ------ .../shared/licensing/license_checks.ts | 17 -- .../shared/licensing/license_context.test.tsx | 24 --- .../shared/licensing/license_context.tsx | 29 ---- .../shared/licensing/licensing_logic.test.ts | 161 ++++++++++++++++++ .../shared/licensing/licensing_logic.ts | 82 +++++++++ .../shared/not_found/not_found.test.tsx | 14 +- .../shared/not_found/not_found.tsx | 9 +- .../enterprise_search/public/plugin.ts | 54 +++--- 19 files changed, 338 insertions(+), 204 deletions(-) rename x-pack/plugins/enterprise_search/public/applications/__mocks__/{license_context.mock.ts => licensing_logic.mock.ts} (79%) delete mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts delete mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts delete mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts index f66235ff44c6aa..88a900f69c5ecf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts @@ -6,7 +6,7 @@ export { mockHistory, mockLocation } from './react_router_history.mock'; export { mockKibanaContext } from './kibana_context.mock'; -export { mockLicenseContext } from './license_context.mock'; +export { mockLicensingValues } from './licensing_logic.mock'; export { mockHttpValues } from './http_logic.mock'; export { mockFlashMessagesValues, mockFlashMessagesActions } from './flash_messages_logic.mock'; export { mockAllValues, mockAllActions, setMockValues } from './kea.mock'; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts index 8e6b0baa5fc00f..bad6beaa1652ee 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts @@ -5,13 +5,17 @@ */ /** + * Combine all shared mock values/actions into a single obj + * * NOTE: These variable names MUST start with 'mock*' in order for * Jest to accept its use within a jest.mock() */ +import { mockLicensingValues } from './licensing_logic.mock'; import { mockHttpValues } from './http_logic.mock'; import { mockFlashMessagesValues, mockFlashMessagesActions } from './flash_messages_logic.mock'; export const mockAllValues = { + ...mockLicensingValues, ...mockHttpValues, ...mockFlashMessagesValues, }; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/license_context.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/licensing_logic.mock.ts similarity index 79% rename from x-pack/plugins/enterprise_search/public/applications/__mocks__/license_context.mock.ts rename to x-pack/plugins/enterprise_search/public/applications/__mocks__/licensing_logic.mock.ts index 7c37ecc7cde1b8..51b32e7a877b26 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/license_context.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/licensing_logic.mock.ts @@ -6,6 +6,8 @@ import { licensingMock } from '../../../../licensing/public/mocks'; -export const mockLicenseContext = { +export const mockLicensingValues = { license: licensingMock.createLicense(), + hasPlatinumLicense: false, + hasGoldLicense: false, }; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_context.mock.tsx b/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_context.mock.tsx index 5e56f17c8e7f31..646c3104c286fa 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_context.mock.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_context.mock.tsx @@ -15,8 +15,6 @@ import { getContext, resetContext } from 'kea'; import { I18nProvider } from '@kbn/i18n/react'; import { KibanaContext } from '../'; import { mockKibanaContext } from './kibana_context.mock'; -import { LicenseContext } from '../shared/licensing'; -import { mockLicenseContext } from './license_context.mock'; /** * This helper mounts a component with all the contexts/providers used @@ -34,9 +32,7 @@ export const mountWithContext = (children: React.ReactNode, context?: object) => return mount( - - {children} - + {children} ); diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts index 3a2193db646de4..df9e58994e36b4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts @@ -9,11 +9,10 @@ * Jest to accept its use within a jest.mock() */ import { mockKibanaContext } from './kibana_context.mock'; -import { mockLicenseContext } from './license_context.mock'; jest.mock('react', () => ({ ...(jest.requireActual('react') as object), - useContext: jest.fn(() => ({ ...mockKibanaContext, ...mockLicenseContext })), + useContext: jest.fn(() => ({ ...mockKibanaContext })), useEffect: jest.fn((fn) => fn()), // Calls on mount/every update - use mount for more complex behavior })); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx index 928d92d7910940..44afce96c1a6c0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx @@ -82,9 +82,11 @@ describe('EngineOverview', () => { describe('when on a platinum license', () => { it('renders a 2nd meta engines table & makes a 2nd meta engines API call', async () => { - const wrapper = await mountWithAsyncContext(, { - license: { type: 'platinum', isActive: true }, + setMockValues({ + hasPlatinumLicense: true, + http: { ...mockHttpValues.http, get: mockApi }, }); + const wrapper = await mountWithAsyncContext(); expect(wrapper.find(EngineTable)).toHaveLength(2); expect(mockApi).toHaveBeenNthCalledWith(2, '/api/app_search/engines', { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx index c0aedbe7dc6b42..0cb9ba106dbb82 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useValues } from 'kea'; import { EuiPageContent, @@ -19,7 +19,7 @@ import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chro import { SendAppSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; import { FlashMessages } from '../../../shared/flash_messages'; import { HttpLogic } from '../../../shared/http'; -import { LicenseContext, ILicenseContext, hasPlatinumLicense } from '../../../shared/licensing'; +import { LicensingLogic } from '../../../shared/licensing'; import { EngineIcon } from './assets/engine_icon'; import { MetaEngineIcon } from './assets/meta_engine_icon'; @@ -40,7 +40,7 @@ interface ISetEnginesCallbacks { export const EngineOverview: React.FC = () => { const { http } = useValues(HttpLogic); - const { license } = useContext(LicenseContext) as ILicenseContext; + const { hasPlatinumLicense } = useValues(LicensingLogic); const [isLoading, setIsLoading] = useState(true); const [engines, setEngines] = useState([]); @@ -72,13 +72,13 @@ export const EngineOverview: React.FC = () => { }, [enginesPage]); useEffect(() => { - if (hasPlatinumLicense(license)) { + if (hasPlatinumLicense) { const params = { type: 'meta', pageIndex: metaEnginesPage }; const callbacks = { setResults: setMetaEngines, setResultsTotal: setMetaEnginesTotal }; setEnginesData(params, callbacks); } - }, [license, metaEnginesPage]); + }, [hasPlatinumLicense, metaEnginesPage]); if (isLoading) return ; if (!engines.length) return ; diff --git a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx index 053c450ab925ef..6ee63ee22cae2b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx @@ -6,7 +6,6 @@ import React from 'react'; -import { AppMountParameters } from 'src/core/public'; import { coreMock } from 'src/core/public/mocks'; import { licensingMock } from '../../../licensing/public/mocks'; @@ -15,37 +14,38 @@ import { AppSearch } from './app_search'; import { WorkplaceSearch } from './workplace_search'; describe('renderApp', () => { - let params: AppMountParameters; - const core = coreMock.createStart(); - const plugins = { - licensing: licensingMock.createSetup(), + const kibanaDeps = { + params: coreMock.createAppMountParamters(), + core: coreMock.createStart(), + plugins: { licensing: licensingMock.createStart() }, + } as any; + const pluginData = { + config: {}, + data: {}, } as any; - const config = {}; - const data = {} as any; beforeEach(() => { jest.clearAllMocks(); - params = coreMock.createAppMountParamters(); }); it('mounts and unmounts UI', () => { const MockApp = () =>
Hello world!
; - const unmount = renderApp(MockApp, params, core, plugins, config, data); - expect(params.element.querySelector('.hello-world')).not.toBeNull(); + const unmount = renderApp(MockApp, kibanaDeps, pluginData); + expect(kibanaDeps.params.element.querySelector('.hello-world')).not.toBeNull(); unmount(); - expect(params.element.innerHTML).toEqual(''); + expect(kibanaDeps.params.element.innerHTML).toEqual(''); }); it('renders AppSearch', () => { - renderApp(AppSearch, params, core, plugins, config, data); - expect(params.element.querySelector('.setupGuide')).not.toBeNull(); + renderApp(AppSearch, kibanaDeps, pluginData); + expect(kibanaDeps.params.element.querySelector('.setupGuide')).not.toBeNull(); }); it('renders WorkplaceSearch', () => { - renderApp(WorkplaceSearch, params, core, plugins, config, data); - expect(params.element.querySelector('.setupGuide')).not.toBeNull(); + renderApp(WorkplaceSearch, kibanaDeps, pluginData); + expect(kibanaDeps.params.element.querySelector('.setupGuide')).not.toBeNull(); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index 0869ef7b22729e..4a25ecf6067cc3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -14,8 +14,8 @@ import { getContext, resetContext } from 'kea'; import { I18nProvider } from '@kbn/i18n/react'; import { AppMountParameters, CoreStart, ApplicationStart, ChromeBreadcrumb } from 'src/core/public'; -import { ClientConfigType, ClientData, PluginsSetup } from '../plugin'; -import { LicenseProvider } from './shared/licensing'; +import { PluginsStart, ClientConfigType, ClientData } from '../plugin'; +import { mountLicensingLogic } from './shared/licensing'; import { mountHttpLogic } from './shared/http'; import { mountFlashMessagesLogic } from './shared/flash_messages'; import { IExternalUrl } from './shared/enterprise_search_url'; @@ -39,15 +39,18 @@ export const KibanaContext = React.createContext({}); export const renderApp = ( App: React.FC, - params: AppMountParameters, - core: CoreStart, - plugins: PluginsSetup, - config: ClientConfigType, - { externalUrl, errorConnecting, ...initialData }: ClientData + { params, core, plugins }: { params: AppMountParameters; core: CoreStart; plugins: PluginsStart }, + { config, data }: { config: ClientConfigType; data: ClientData } ) => { + const { externalUrl, errorConnecting, ...initialData } = data; + resetContext({ createStore: true }); const store = getContext().store as Store; + const unmountLicensingLogic = mountLicensingLogic({ + license$: plugins.licensing.license$, + }); + const unmountHttpLogic = mountHttpLogic({ http: core.http, errorConnecting, @@ -67,19 +70,18 @@ export const renderApp = ( setDocTitle: core.chrome.docTitle.change, }} > - - - - - - - + + + + + , params.element ); return () => { ReactDOM.unmountComponentAtNode(params.element); + unmountLicensingLogic(); unmountHttpLogic(); unmountFlashMessagesLogic(); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/index.ts index 29c11ffa1cef8c..4e371b337c40a0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { LicenseContext, LicenseProvider, ILicenseContext } from './license_context'; -export { hasPlatinumLicense, hasGoldLicense } from './license_checks'; +export { LicensingLogic, mountLicensingLogic } from './licensing_logic'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts deleted file mode 100644 index 40f0f6380c21cd..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { hasPlatinumLicense, hasGoldLicense } from './license_checks'; - -describe('hasPlatinumLicense', () => { - it('is true for platinum licenses', () => { - expect(hasPlatinumLicense({ isActive: true, type: 'platinum' } as any)).toEqual(true); - }); - - it('is true for enterprise licenses', () => { - expect(hasPlatinumLicense({ isActive: true, type: 'enterprise' } as any)).toEqual(true); - }); - - it('is true for trial licenses', () => { - expect(hasPlatinumLicense({ isActive: true, type: 'platinum' } as any)).toEqual(true); - }); - - it('is false if the current license is expired', () => { - expect(hasPlatinumLicense({ isActive: false, type: 'platinum' } as any)).toEqual(false); - expect(hasPlatinumLicense({ isActive: false, type: 'enterprise' } as any)).toEqual(false); - expect(hasPlatinumLicense({ isActive: false, type: 'trial' } as any)).toEqual(false); - }); - - it('is false for licenses below platinum', () => { - expect(hasPlatinumLicense({ isActive: true, type: 'basic' } as any)).toEqual(false); - expect(hasPlatinumLicense({ isActive: false, type: 'standard' } as any)).toEqual(false); - expect(hasPlatinumLicense({ isActive: true, type: 'gold' } as any)).toEqual(false); - }); -}); - -describe('hasGoldLicense', () => { - it('is true for gold+ and trial licenses', () => { - expect(hasGoldLicense({ isActive: true, type: 'gold' } as any)).toEqual(true); - expect(hasGoldLicense({ isActive: true, type: 'platinum' } as any)).toEqual(true); - expect(hasGoldLicense({ isActive: true, type: 'enterprise' } as any)).toEqual(true); - expect(hasGoldLicense({ isActive: true, type: 'trial' } as any)).toEqual(true); - }); - - it('is false if the current license is expired', () => { - expect(hasGoldLicense({ isActive: false, type: 'gold' } as any)).toEqual(false); - expect(hasGoldLicense({ isActive: false, type: 'platinum' } as any)).toEqual(false); - expect(hasGoldLicense({ isActive: false, type: 'enterprise' } as any)).toEqual(false); - expect(hasGoldLicense({ isActive: false, type: 'trial' } as any)).toEqual(false); - }); - - it('is false for licenses below gold', () => { - expect(hasGoldLicense({ isActive: true, type: 'basic' } as any)).toEqual(false); - expect(hasGoldLicense({ isActive: false, type: 'standard' } as any)).toEqual(false); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts deleted file mode 100644 index d13d0909243be7..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { ILicense } from '../../../../../licensing/public'; - -export const hasPlatinumLicense = (license?: ILicense) => { - const qualifyingLicenses = ['platinum', 'enterprise', 'trial']; - return license?.isActive && qualifyingLicenses.includes(license?.type as string); -}; - -export const hasGoldLicense = (license?: ILicense) => { - const qualifyingLicenses = ['gold', 'platinum', 'enterprise', 'trial']; - return license?.isActive && qualifyingLicenses.includes(license?.type as string); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx deleted file mode 100644 index c65474ec1f5900..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useContext } from 'react'; - -import { mountWithContext } from '../../__mocks__'; -import { LicenseContext, ILicenseContext } from './'; - -describe('LicenseProvider', () => { - const MockComponent: React.FC = () => { - const { license } = useContext(LicenseContext) as ILicenseContext; - return
{license?.type}
; - }; - - it('renders children', () => { - const wrapper = mountWithContext(, { license: { type: 'basic' } }); - - expect(wrapper.find('.license-test')).toHaveLength(1); - expect(wrapper.text()).toEqual('basic'); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx deleted file mode 100644 index 9b47959ff75447..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import useObservable from 'react-use/lib/useObservable'; -import { Observable } from 'rxjs'; - -import { ILicense } from '../../../../../licensing/public'; - -export interface ILicenseContext { - license: ILicense; -} -interface ILicenseContextProps { - license$: Observable; - children: React.ReactNode; -} - -export const LicenseContext = React.createContext({}); - -export const LicenseProvider: React.FC = ({ license$, children }) => { - // Listen for changes to license subscription - const license = useObservable(license$); - - // Render rest of application and pass down license via context - return ; -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts new file mode 100644 index 00000000000000..153a5ae7654682 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts @@ -0,0 +1,161 @@ +/* + * 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 { resetContext } from 'kea'; +import { BehaviorSubject } from 'rxjs'; + +import { licensingMock } from '../../../../../licensing/public/mocks'; + +import { LicensingLogic, mountLicensingLogic } from './licensing_logic'; + +describe('LicensingLogic', () => { + const mockLicense = licensingMock.createLicense(); + const mockLicense$ = new BehaviorSubject(mockLicense); + const mount = () => mountLicensingLogic({ license$: mockLicense$ }); + + beforeEach(() => { + jest.clearAllMocks(); + resetContext({}); + }); + + describe('setLicense()', () => { + it('sets license value', () => { + mount(); + LicensingLogic.actions.setLicense('test' as any); + expect(LicensingLogic.values.license).toEqual('test'); + }); + }); + + describe('setLicenseSubscription()', () => { + it('sets licenseSubscription value', () => { + mount(); + LicensingLogic.actions.setLicenseSubscription('test' as any); + expect(LicensingLogic.values.licenseSubscription).toEqual('test'); + }); + }); + + describe('licensing subscription', () => { + describe('on mount', () => { + it('subscribes to the license observable', () => { + mount(); + expect(LicensingLogic.values.license).toEqual(mockLicense); + expect(LicensingLogic.values.licenseSubscription).not.toBeNull(); + }); + }); + + describe('on subscription update', () => { + it('updates the license value', () => { + mount(); + + const nextMockLicense = licensingMock.createLicense({ license: { status: 'invalid' } }); + mockLicense$.next(nextMockLicense); + + expect(LicensingLogic.values.license).toEqual(nextMockLicense); + }); + }); + + describe('on unmount', () => { + it('unsubscribes to the license observable', () => { + const mockUnsubscribe = jest.fn(); + const unmount = mountLicensingLogic({ + license$: { subscribe: () => ({ unsubscribe: mockUnsubscribe }) } as any, + }); + unmount(); + expect(mockUnsubscribe).toHaveBeenCalled(); + }); + + it('does not crash if no subscription exists', () => { + const unmount = mount(); + LicensingLogic.actions.setLicenseSubscription(null as any); + unmount(); + }); + }); + }); + + describe('license check selectors', () => { + beforeEach(() => { + mount(); + }); + + const updateLicense = (license: any) => { + const updatedLicense = licensingMock.createLicense({ license }); + mockLicense$.next(updatedLicense); + }; + + describe('hasPlatinumLicense', () => { + it('is true for platinum+ and trial licenses', () => { + updateLicense({ status: 'active', type: 'platinum' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(true); + + updateLicense({ status: 'active', type: 'enterprise' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(true); + + updateLicense({ status: 'active', type: 'trial' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(true); + }); + + it('is false if the current license is expired', () => { + updateLicense({ status: 'expired', type: 'platinum' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(false); + + updateLicense({ status: 'expired', type: 'enterprise' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(false); + + updateLicense({ status: 'expired', type: 'trial' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(false); + }); + + it('is false for licenses below platinum', () => { + updateLicense({ status: 'active', type: 'basic' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(false); + + updateLicense({ status: 'active', type: 'standard' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(false); + + updateLicense({ status: 'active', type: 'gold' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(false); + }); + }); + + describe('hasGoldLicense', () => { + it('is true for gold+ and trial licenses', () => { + updateLicense({ status: 'active', type: 'gold' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(true); + + updateLicense({ status: 'active', type: 'platinum' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(true); + + updateLicense({ status: 'active', type: 'enterprise' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(true); + + updateLicense({ status: 'active', type: 'trial' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(true); + }); + + it('is false if the current license is expired', () => { + updateLicense({ status: 'expired', type: 'gold' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(false); + + updateLicense({ status: 'expired', type: 'platinum' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(false); + + updateLicense({ status: 'expired', type: 'enterprise' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(false); + + updateLicense({ status: 'expired', type: 'trial' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(false); + }); + + it('is false for licenses below gold', () => { + updateLicense({ status: 'active', type: 'basic' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(false); + + updateLicense({ status: 'active', type: 'standard' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(false); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts new file mode 100644 index 00000000000000..ae31b2ec6168a4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts @@ -0,0 +1,82 @@ +/* + * 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 { kea, MakeLogicType } from 'kea'; +import { Observable, Subscription } from 'rxjs'; + +import { ILicense } from '../../../../../licensing/public'; + +export interface ILicensingValues { + license: ILicense | null; + licenseSubscription: Subscription | null; + hasPlatinumLicense: boolean; + hasGoldLicense: boolean; +} +export interface ILicensingActions { + setLicense(license: ILicense): ILicense; + setLicenseSubscription(licenseSubscription: Subscription): Subscription; +} + +export const LicensingLogic = kea>({ + path: ['enterprise_search', 'licensing_logic'], + actions: { + setLicense: (license) => license, + setLicenseSubscription: (licenseSubscription) => licenseSubscription, + }, + reducers: { + license: [ + null, + { + setLicense: (_, license) => license, + }, + ], + licenseSubscription: [ + null, + { + setLicenseSubscription: (_, licenseSubscription) => licenseSubscription, + }, + ], + }, + selectors: { + hasPlatinumLicense: [ + (selectors) => [selectors.license], + (license) => { + const qualifyingLicenses = ['platinum', 'enterprise', 'trial']; + return license?.isActive && qualifyingLicenses.includes(license?.type); + }, + ], + hasGoldLicense: [ + (selectors) => [selectors.license], + (license) => { + const qualifyingLicenses = ['gold', 'platinum', 'enterprise', 'trial']; + return license?.isActive && qualifyingLicenses.includes(license?.type); + }, + ], + }, + events: ({ props, actions, values }) => ({ + afterMount: () => { + const licenseSubscription = props.license$.subscribe(async (license: ILicense) => { + actions.setLicense(license); + }); + actions.setLicenseSubscription(licenseSubscription); + }, + beforeUnmount: () => { + if (values.licenseSubscription) values.licenseSubscription.unsubscribe(); + }, + }), +}); + +/** + * Mount/props helper + */ +interface ILicensingLogicProps { + license$: Observable; +} +export const mountLicensingLogic = (props: ILicensingLogicProps) => { + LicensingLogic(props); + const unmount = LicensingLogic.mount(); + return unmount; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.test.tsx index ce9071ad7b9d0c..62c0af31cffd9a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.test.tsx @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../__mocks__/shallow_usecontext.mock'; +import '../../__mocks__/kea.mock'; -import React, { useContext } from 'react'; +import React from 'react'; +import { useValues } from 'kea'; import { shallow } from 'enzyme'; import { EuiButton as EuiButtonExternal, EuiEmptyPrompt } from '@elastic/eui'; @@ -18,13 +19,6 @@ import { WorkplaceSearchLogo } from './assets/workplace_search_logo'; import { NotFound } from './'; describe('NotFound', () => { - const basicLicense = { isActive: true, type: 'basic' }; - const goldLicense = { isActive: true, type: 'gold' }; - - beforeEach(() => { - (useContext as jest.Mock).mockImplementation(() => ({ license: basicLicense })); - }); - it('renders an App Search 404 view', () => { const wrapper = shallow(); const prompt = wrapper.find(EuiEmptyPrompt).dive().shallow(); @@ -50,7 +44,7 @@ describe('NotFound', () => { }); it('changes the support URL if the user has a gold+ license', () => { - (useContext as jest.Mock).mockImplementation(() => ({ license: goldLicense })); + (useValues as jest.Mock).mockReturnValueOnce({ hasGoldLicense: true }); const wrapper = shallow(); const prompt = wrapper.find(EuiEmptyPrompt).dive().shallow(); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx index bd988854225fbd..40bb5efcc6330b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React from 'react'; +import { useValues } from 'kea'; import { i18n } from '@kbn/i18n'; import { EuiPageContent, @@ -24,7 +25,7 @@ import { import { EuiButton } from '../react_router_helpers'; import { SetAppSearchChrome, SetWorkplaceSearchChrome } from '../kibana_chrome'; import { SendAppSearchTelemetry, SendWorkplaceSearchTelemetry } from '../telemetry'; -import { LicenseContext, ILicenseContext, hasGoldLicense } from '../licensing'; +import { LicensingLogic } from '../licensing'; import { AppSearchLogo } from './assets/app_search_logo'; import { WorkplaceSearchLogo } from './assets/workplace_search_logo'; @@ -39,8 +40,8 @@ interface NotFoundProps { } export const NotFound: React.FC = ({ product = {} }) => { - const { license } = useContext(LicenseContext) as ILicenseContext; - const supportUrl = hasGoldLicense(license) ? LICENSED_SUPPORT_URL : product.SUPPORT_URL; + const { hasGoldLicense } = useValues(LicensingLogic); + const supportUrl = hasGoldLicense ? LICENSED_SUPPORT_URL : product.SUPPORT_URL; let Logo; let SetPageChrome; diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index c23bb23be3979e..f59ec830c812fb 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -7,7 +7,6 @@ import { AppMountParameters, CoreSetup, - CoreStart, HttpSetup, Plugin, PluginInitializerContext, @@ -17,7 +16,7 @@ import { FeatureCatalogueCategory, HomePublicPluginSetup, } from '../../../../src/plugins/home/public'; -import { LicensingPluginSetup } from '../../licensing/public'; +import { LicensingPluginStart } from '../../licensing/public'; import { APP_SEARCH_PLUGIN, ENTERPRISE_SEARCH_PLUGIN, @@ -36,7 +35,9 @@ export interface ClientData extends IInitialAppData { export interface PluginsSetup { home?: HomePublicPluginSetup; - licensing: LicensingPluginSetup; +} +export interface PluginsStart { + licensing: LicensingPluginStart; } export class EnterpriseSearchPlugin implements Plugin { @@ -57,16 +58,17 @@ export class EnterpriseSearchPlugin implements Plugin { appRoute: ENTERPRISE_SEARCH_PLUGIN.URL, category: DEFAULT_APP_CATEGORIES.enterpriseSearch, mount: async (params: AppMountParameters) => { - const [coreStart] = await core.getStartServices(); - const { chrome } = coreStart; - chrome.docTitle.change(ENTERPRISE_SEARCH_PLUGIN.NAME); + const kibanaDeps = await this.getKibanaDeps(core, params); + const pluginData = this.getPluginData(); - await this.getInitialData(coreStart.http); + const { chrome, http } = kibanaDeps.core; + chrome.docTitle.change(ENTERPRISE_SEARCH_PLUGIN.NAME); + await this.getInitialData(http); const { renderApp } = await import('./applications'); const { EnterpriseSearch } = await import('./applications/enterprise_search'); - return renderApp(EnterpriseSearch, params, coreStart, plugins, this.config, this.data); + return renderApp(EnterpriseSearch, kibanaDeps, pluginData); }, }); @@ -77,16 +79,17 @@ export class EnterpriseSearchPlugin implements Plugin { appRoute: APP_SEARCH_PLUGIN.URL, category: DEFAULT_APP_CATEGORIES.enterpriseSearch, mount: async (params: AppMountParameters) => { - const [coreStart] = await core.getStartServices(); - const { chrome } = coreStart; - chrome.docTitle.change(APP_SEARCH_PLUGIN.NAME); + const kibanaDeps = await this.getKibanaDeps(core, params); + const pluginData = this.getPluginData(); - await this.getInitialData(coreStart.http); + const { chrome, http } = kibanaDeps.core; + chrome.docTitle.change(APP_SEARCH_PLUGIN.NAME); + await this.getInitialData(http); const { renderApp } = await import('./applications'); const { AppSearch } = await import('./applications/app_search'); - return renderApp(AppSearch, params, coreStart, plugins, this.config, this.data); + return renderApp(AppSearch, kibanaDeps, pluginData); }, }); @@ -97,11 +100,12 @@ export class EnterpriseSearchPlugin implements Plugin { appRoute: WORKPLACE_SEARCH_PLUGIN.URL, category: DEFAULT_APP_CATEGORIES.enterpriseSearch, mount: async (params: AppMountParameters) => { - const [coreStart] = await core.getStartServices(); - const { chrome } = coreStart; - chrome.docTitle.change(WORKPLACE_SEARCH_PLUGIN.NAME); + const kibanaDeps = await this.getKibanaDeps(core, params); + const pluginData = this.getPluginData(); - await this.getInitialData(coreStart.http); + const { chrome, http } = kibanaDeps.core; + chrome.docTitle.change(APP_SEARCH_PLUGIN.NAME); + await this.getInitialData(http); const { renderApp, renderHeaderActions } = await import('./applications'); const { WorkplaceSearch } = await import('./applications/workplace_search'); @@ -113,7 +117,7 @@ export class EnterpriseSearchPlugin implements Plugin { renderHeaderActions(WorkplaceSearchHeaderActions, element, this.data.externalUrl) ); - return renderApp(WorkplaceSearch, params, coreStart, plugins, this.config, this.data); + return renderApp(WorkplaceSearch, kibanaDeps, pluginData); }, }); @@ -149,10 +153,22 @@ export class EnterpriseSearchPlugin implements Plugin { } } - public start(core: CoreStart) {} + public start() {} public stop() {} + private async getKibanaDeps(core: CoreSetup, params: AppMountParameters) { + // Helper for using start dependencies on mount (instead of setup dependencies) + // and for grouping Kibana-related args together (vs. plugin-specific args) + const [coreStart, pluginsStart] = await core.getStartServices(); + return { params, core: coreStart, plugins: pluginsStart as PluginsStart }; + } + + private getPluginData() { + // Small helper for grouping plugin data related args together + return { config: this.config, data: this.data }; + } + private async getInitialData(http: HttpSetup) { if (!this.config.host) return; // No API to call if (this.hasInitialized) return; // We've already made an initial call From 792db1726adce8a2232edfb831bab7bd9966ff98 Mon Sep 17 00:00:00 2001 From: gchaps <33642766+gchaps@users.noreply.github.com> Date: Wed, 23 Sep 2020 11:52:04 -0700 Subject: [PATCH 14/35] [DOCS] Redirects CCR and Remote Clusters docs to ES reference (#77909) * [DOCS] Redirects CRR and Remote Clusters docs to ES reference * [DOCS] Updates link for remote clusters --- docs/management/images/add_remote_cluster.png | Bin 254288 -> 0 bytes .../management/images/auto_follow_pattern.png | Bin 273420 -> 0 bytes .../cross-cluster-replication-list-view.png | Bin 117230 -> 0 bytes .../images/remote-clusters-list-view.png | Bin 96631 -> 0 bytes docs/management/managing-ccr.asciidoc | 80 ------------------ .../managing-remote-clusters.asciidoc | 50 ----------- docs/redirects.asciidoc | 12 +++ docs/user/management.asciidoc | 8 +- 8 files changed, 14 insertions(+), 136 deletions(-) delete mode 100755 docs/management/images/add_remote_cluster.png delete mode 100755 docs/management/images/auto_follow_pattern.png delete mode 100755 docs/management/images/cross-cluster-replication-list-view.png delete mode 100755 docs/management/images/remote-clusters-list-view.png delete mode 100644 docs/management/managing-ccr.asciidoc delete mode 100644 docs/management/managing-remote-clusters.asciidoc diff --git a/docs/management/images/add_remote_cluster.png b/docs/management/images/add_remote_cluster.png deleted file mode 100755 index 160d29b741c6293151973078123cf5eab5996e00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 254288 zcmeFYc{J4T|2M8w+UPBn&`{ahP`1KYN|CIklD(33EHTD5GbEx771?Kyt-_FHFpMcG zyRi?38A5|GW*7{{jQiF5`~7@A-`~04zrXKupL08NoHNVonrmLy^Ljj=&&T6&y|A({ z7TznlS3p2O*yQ@vTLJcfNf>Vv66aKE0R4?ihq@6Nx(pSX6-;OpL#ACI33 z)8*nG{2aEv|K#!xN@*1a7VyZ8sLY|vAQqNNg$~H6)9-w9FmS>`2W8k^hI%p&_51#+vId3 z`^mE@+kbxdQ_-&jq^*_tm}&LD{%QIj%a7)Xq4hg-)rl>ErLq4!$crMyT4u$;4_kCw zkPjNYXs|Ha{)YT%_Fd*Xg=z=7=0Eh@mDY>C9T{8f=j& z)PFg|(jW=jg*!DqzB0LPP)4sT9=lU}?z#ZaRK47pP9w4d}zDMow_Dhn}WVM zMMsN6L-U2u^WGEd`2C4Wke_BDBL5lgl-h3M9^$rtzmVP0eilrcukC+E)7D3xo6!3AI|9dr zCba(N^Q6#dS~TrH?_2#AMgPyQG)OZJvG||g9Bio}9`k&x7 zu)X$wAed9WvA}9-)^{-uN}UZJo<$FujBoUo*vl`3bfNYoIw3cP<6WD6cK&E^7FTw7 z!x!Y2F-V^WAfa;tr&JT9b>9sXk%rMaO~&!bYim0!{=Mx(kAqdBCPHuAc&QHI=Wj-A zg?HUmsb9urYC-Qx>xVmhh<9&}zaL6i6~elt@o60osilHKBCSFzQbVhd&1e%aOyE#j zaq&3`^^jCXZC~DSN`U6_F~xu3;uG*+Xkg}>%V`x$%&q!yQ8c78Rjd8AnTiS4hkQbV zS_;vL_v~{�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�yP;87%zA{Pjk$mPZw8@DX0Ed8y?X0z*~%?7 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 z%0A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 -qBF8;j$Wu|JsSbZ}=H5@5 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 diff --git a/docs/management/images/cross-cluster-replication-list-view.png b/docs/management/images/cross-cluster-replication-list-view.png deleted file mode 100755 index 4c45174cff7f12f8b066b9d62ed1c3762ccec02e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117230 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 h Data > Cross-Cluster Replication*. - -[role="screenshot"] -image::images/cross-cluster-replication-list-view.png[][Cross-cluster replication list view] - -[float] -=== Prerequisites - -* You must have a {ref}/modules-remote-clusters.html[remote cluster]. -* Leader indices must meet {ref}/ccr-requirements.html[these requirements]. -* The Elasticsearch version of the local cluster must be the same as or newer than the remote cluster. -Refer to {ref}/ccr-overview.html[this document] for more information. - -[float] -=== Required permissions - -The `manage` and `manage_ccr` cluster privileges are required to access *Cross-Cluster Replication*. - -You can add these privileges in *Stack Management > Security > Roles*. - -[float] -[[configure-replication]] -=== Configure replication - -Replication requires a leader index, the index being replicated, and a -follower index, which will contain the leader index's replicated data. -The follower index is passive in that it can read requests and searches, -but cannot accept direct writes. Only the leader index is active for direct writes. - -You can configure follower indices in two ways: - -* Create specific follower indices -* Create follower indices from an auto-follow pattern - -[float] -==== Create specific follower indices - -To replicate data from existing indices, or set up local followers on a case-by-case basis, -go to *Follower indices*. When you create the follower index, you must reference the -remote cluster and the leader index that you created in the remote cluster. - -[role="screenshot"] -image::images/follower_indices.png[][UI for adding follower indices] - -[float] -==== Create follower indices from an auto-follow pattern - -To automatically detect and follow new indices when they are created on a remote cluster, -go to *Auto-follow patterns*. Creating an auto-follow pattern is useful when you have -time series data, like event logs, on the remote cluster that is created or rolled over on a daily basis. - -When creating the pattern, you must reference the remote cluster that you -connected to your local cluster. You must also specify a collection of index patterns -that match the indices you want to automatically follow. - -Once you configure an -auto-follow pattern, any time a new index with a name that matches the pattern is -created in the remote cluster, a follower index is automatically configured in the local cluster. - -[role="screenshot"] -image::images/auto_follow_pattern.png[UI for adding an auto-follow pattern] - -[float] -[[manage-replication]] -=== Manage replication - -Use the list views in *Cross-Cluster Replication* to monitor whether the replication is active and -pause and resume replication. You can also edit and remove the follower indices and auto-follow patterns. - -For an example of cross-cluster replication, -refer to https://www.elastic.co/blog/bi-directional-replication-with-elasticsearch-cross-cluster-replication-ccr[Bi-directional replication with Elasticsearch cross-cluster replication]. diff --git a/docs/management/managing-remote-clusters.asciidoc b/docs/management/managing-remote-clusters.asciidoc deleted file mode 100644 index 92e0fa822b0567..00000000000000 --- a/docs/management/managing-remote-clusters.asciidoc +++ /dev/null @@ -1,50 +0,0 @@ -[[working-remote-clusters]] -== Remote Clusters - -Use *Remote Clusters* to establish a unidirectional -connection from your cluster to other clusters. This functionality is -required for {ref}/xpack-ccr.html[cross-cluster replication] and -{ref}/modules-cross-cluster-search.html[cross-cluster search]. - -To get started, open the menu, then go to *Stack Management > Data > Remote Clusters*. - -[role="screenshot"] -image::images/remote-clusters-list-view.png[Remote Clusters list view, including Add a remote cluster button] - -[float] -=== Required permissions - -The `manage` cluster privilege is required to access *Remote Clusters*. - -You can add this privilege in *Stack Management > Security > Roles*. - -[float] -[[managing-remote-clusters]] -=== Add a remote cluster - -A {ref}/modules-remote-clusters.html[remote cluster] connection works by configuring a remote cluster and -connecting to a limited number of nodes, called {ref}/modules-remote-clusters.html#sniff-mode[seed nodes], -in that cluster. -Alternatively, you can define a single proxy address for the remote cluster. - -By default, a cross-cluster request, such as a cross-cluster search or -replication request, fails if any cluster in the request is unavailable. -To skip a cluster when its unavailable, -set *Skip if unavailable* to true. - -Once you add a remote cluster, you can configure <> -to reproduce indices in the remote cluster on a local cluster. - -[role="screenshot"] -image::images/add_remote_cluster.png[][UI for adding a remote cluster] - -To create an index pattern to search across clusters, -use the same syntax that you’d use in a raw cross-cluster search request in {es}: :. -See <> for examples. - -[float] -[[manage-remote-clusters]] -=== Manage remote clusters - -From the *Remote Clusters* list view, you can drill down into each cluster and -view its status. You can also edit and delete a cluster. diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index 42d1d89145d79c..5067bc08bec99e 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -100,3 +100,15 @@ This content has moved to the <> page. == TSVB This page was deleted. See <>. + +[role="exclude",id="managing-cross-cluster-replication"] +== Cross-Cluster Replication + +This content has moved. See +{ref}/ccr-getting-started.html[Set up cross-cluster replication]. + +[role="exclude",id="working-remote-clusters"] +== Remote clusters + +This content has moved. See +{ref}/ccr-getting-started.html#ccr-getting-started-remote-cluster[Connect to a remote cluster]. diff --git a/docs/user/management.asciidoc b/docs/user/management.asciidoc index bc96463f6efbad..e0d550a15a907b 100644 --- a/docs/user/management.asciidoc +++ b/docs/user/management.asciidoc @@ -58,12 +58,12 @@ years of historical data in combination with your raw data. | {ref}/transforms.html[Transforms] |Use transforms to pivot existing {es} indices into summarized or entity-centric indices. -| <> +| {ref}/ccr-getting-started.html[Cross-Cluster Replication] |Replicate indices on a remote cluster and copy them to a follower index on a local cluster. This is important for disaster recovery. It also keeps data local for faster queries. -| <> +| {ref}/ccr-getting-started.html#ccr-getting-started-remote-cluster[Remote Clusters] |Manage your remote clusters for use with cross-cluster search and cross-cluster replication. You can add and remove remote clusters, and check their connectivity. |=== @@ -180,8 +180,6 @@ include::{kib-repo-dir}/management/alerting/connector-management.asciidoc[] include::{kib-repo-dir}/management/managing-beats.asciidoc[] -include::{kib-repo-dir}/management/managing-ccr.asciidoc[] - include::{kib-repo-dir}/management/index-lifecycle-policies/intro-to-lifecycle-policies.asciidoc[] include::{kib-repo-dir}/management/index-lifecycle-policies/create-policy.asciidoc[] @@ -200,8 +198,6 @@ include::{kib-repo-dir}/management/managing-licenses.asciidoc[] include::{kib-repo-dir}/management/numeral.asciidoc[] -include::{kib-repo-dir}/management/managing-remote-clusters.asciidoc[] - include::{kib-repo-dir}/management/rollups/create_and_manage_rollups.asciidoc[] include::{kib-repo-dir}/management/managing-saved-objects.asciidoc[] From f9e0679682fd0e0fbe3b5664adc72da8d15ada8b Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper Date: Wed, 23 Sep 2020 13:53:16 -0500 Subject: [PATCH 15/35] [Metrics UI] Minor fixes to inventory timeline (#78226) --- .../components/waffle/interval_label.tsx | 2 +- .../inventory_view/hooks/use_timeline.ts | 39 ++++++++++++------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/interval_label.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/interval_label.tsx index 6e031c8396f076..dbbfb0f49c0e9c 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/interval_label.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/interval_label.tsx @@ -22,7 +22,7 @@ export const IntervalLabel = ({ intervalAsString }: Props) => {

    diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_timeline.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_timeline.ts index 650eda0362d9e2..acf9011ac7ddd4 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_timeline.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_timeline.ts @@ -28,21 +28,28 @@ const ONE_MINUTE = 60; const ONE_HOUR = ONE_MINUTE * 60; const ONE_DAY = ONE_HOUR * 24; const ONE_WEEK = ONE_DAY * 7; +const ONE_MONTH = ONE_DAY * 30; + +const getDisplayInterval = (interval: string | undefined) => { + if (interval) { + const intervalInSeconds = getIntervalInSeconds(interval); + if (intervalInSeconds < 300) return '5m'; + } + return interval; +}; const getTimeLengthFromInterval = (interval: string | undefined) => { if (interval) { const intervalInSeconds = getIntervalInSeconds(interval); - const multiplier = - intervalInSeconds < ONE_MINUTE - ? ONE_HOUR / intervalInSeconds - : intervalInSeconds < ONE_HOUR - ? 60 - : intervalInSeconds < ONE_DAY - ? 7 - : intervalInSeconds < ONE_WEEK - ? 30 - : 1; - const timeLength = intervalInSeconds * multiplier; + // Get up to 288 datapoints based on interval + const timeLength = + intervalInSeconds <= ONE_MINUTE * 15 + ? ONE_DAY + : intervalInSeconds <= ONE_MINUTE * 35 + ? ONE_DAY * 3 + : intervalInSeconds <= ONE_HOUR * 2.5 + ? ONE_WEEK + : ONE_MONTH; return { timeLength, intervalInSeconds }; } else { return { timeLength: 0, intervalInSeconds: 0 }; @@ -67,15 +74,19 @@ export function useTimeline( ); }; - const timeLengthResult = useMemo(() => getTimeLengthFromInterval(interval), [interval]); + const displayInterval = useMemo(() => getDisplayInterval(interval), [interval]); + + const timeLengthResult = useMemo(() => getTimeLengthFromInterval(displayInterval), [ + displayInterval, + ]); const { timeLength, intervalInSeconds } = timeLengthResult; const timerange: InfraTimerangeInput = { - interval: interval ?? '', + interval: displayInterval ?? '', to: currentTime + intervalInSeconds * 1000, from: currentTime - timeLength * 1000, - lookbackSize: 0, ignoreLookback: true, + forceInterval: true, }; const { error, loading, response, makeRequest } = useHTTPRequest( From abb1cbfa5f8ae2d387e70b312b709c3abb83b706 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 23 Sep 2020 12:35:54 -0700 Subject: [PATCH 16/35] skip flaky suite (#39842) --- test/functional/apps/discover/_inspector.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/discover/_inspector.js b/test/functional/apps/discover/_inspector.js index 900ad28e14e691..fcb66fbd52cf73 100644 --- a/test/functional/apps/discover/_inspector.js +++ b/test/functional/apps/discover/_inspector.js @@ -34,7 +34,8 @@ export default function ({ getService, getPageObjects }) { return hitsCountStatsRow[STATS_ROW_VALUE_INDEX]; } - describe('inspect', () => { + // FLAKY: https://github.com/elastic/kibana/issues/39842 + describe.skip('inspect', () => { before(async () => { await esArchiver.loadIfNeeded('logstash_functional'); await esArchiver.load('discover'); From a54cc17f0f4e7c364985fc54b48a6f6b1369e28c Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 23 Sep 2020 12:42:16 -0700 Subject: [PATCH 17/35] skip flaky suite (#61612) --- .../security_solution/cypress/integration/url_state.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts index 6c1d73492f30a7..04aecfab4561fe 100644 --- a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts @@ -51,7 +51,8 @@ const ABSOLUTE_DATE = { startTimeTimeline: '2019-08-02T20:03:29.186Z', }; -describe('url state', () => { +// FLAKY: https://github.com/elastic/kibana/issues/61612 +describe.skip('url state', () => { it('sets the global start and end dates from the url', () => { loginAndWaitForPageWithoutDateRange(ABSOLUTE_DATE_RANGE.url); cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON).should( From 94a4e38053aa38738405fab246ed703459fda7b7 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 23 Sep 2020 15:49:52 -0400 Subject: [PATCH 18/35] [Security Solution] Options to select index patterns (#77192) * init commit * lots of cleanup * starting on tests... problems * Ready for review * remove sample data * remove comment and fix type * pr changes * fix type * scratchy * sourcerer in timeline * sourcerer in timeline * wip * moving to redux * working on types * fixed * more adjustments, tests fixed * FF off * pr ready * renaming * url state working, hoc not working * url state working for timeline and default scope * script to build fields for beat doc * refactor sourcerer * refactor host to useSourcerer * refactor network to useSourcerer * refactor overview to useSourcerer * refactor detections to useSourcerer * wip for timelines to remove all useSource * wip indexes timeline * do component tests * start container tests * start container tests * update selection widget of index patterns + remove last useWithSource * add indexeNames in network kpi * fix type errors * fix type * missing merge master * get existing index from config file * fixing broken tests * add saving button to avoid to many queries to be aborted * reducer timeline tests broke * need to rewind * much better * timeline saving index names + clean up url state to only manage default * more test fixing * more test changes * remove all the useWithSource + deprecated the graphql until we delete it in a new PR + delete all the beat doc * default timeline to all index when creation + filter index patterns to make sure you do not add one who we do not know * fix types * test for stateful timeline render * we should not have change that * no chnages + snapshot * fix test + bugs from review * fix uncommon processes indexNames * review III * change design for main page of the sourcerer from design * bug fixes when opening old timeline + implementation of new design * fix circular deps * remove unused attributes for event details * design cleanup * fix api integration test with the new search strategy * add reset + manage accordion state * fix bugs + types issues * cleanup * update docs * review -> remove tooltip when popover is open * cypress fixing * fix for ml_condition_links and url_state cypress tests * add cy wait for race condition in pagination tests * missing plumbing kpi host Co-authored-by: Steph Milovic Co-authored-by: Patryk Kopycinski --- ...ic.indexpatternsservice.getidswithtitle.md | 16 + ...lugins-data-public.indexpatternsservice.md | 1 + .../index_patterns/index_patterns.ts | 19 + src/plugins/data/public/public.api.md | 4 + .../security_solution/common/constants.ts | 3 +- .../common/search_strategy/common/index.ts | 2 +- .../search_strategy/index_fields/index.ts | 81 + .../timeline/events/details/index.ts | 1 - .../common/types/timeline/index.ts | 3 + .../integration/ml_conditional_links.spec.ts | 26 +- .../cypress/integration/pagination.spec.ts | 1 + .../cypress/integration/url_state.spec.ts | 12 +- x-pack/plugins/security_solution/package.json | 1 + .../security_solution/public/app/app.tsx | 18 +- .../public/app/home/index.tsx | 30 +- .../components/case_header_page/index.tsx | 4 +- .../cases/components/case_view/index.tsx | 1 + .../components/alerts_viewer/alerts_table.tsx | 2 + .../common/components/alerts_viewer/index.tsx | 2 + .../common/components/alerts_viewer/types.ts | 2 +- .../drag_drop_context_wrapper.test.tsx | 19 +- .../drag_and_drop/draggable_wrapper.test.tsx | 51 +- .../draggable_wrapper_hover_content.test.tsx | 21 +- .../draggable_wrapper_hover_content.tsx | 27 +- .../drag_and_drop/droppable_wrapper.test.tsx | 45 +- .../events_viewer/events_viewer.test.tsx | 344 +- .../events_viewer/events_viewer.tsx | 4 +- .../components/events_viewer/index.test.tsx | 36 +- .../common/components/events_viewer/index.tsx | 29 +- .../add_exception_modal/index.test.tsx | 15 +- .../exceptions/add_exception_modal/index.tsx | 15 +- .../edit_exception_modal/index.test.tsx | 11 +- .../exceptions/edit_exception_modal/index.tsx | 14 +- .../common/components/header_global/index.tsx | 10 +- .../__snapshots__/index.test.tsx.snap | 3 + .../common/components/header_page/index.tsx | 5 + .../components/last_event_time/index.test.tsx | 8 +- .../components/last_event_time/index.tsx | 83 +- .../matrix_histogram/index.test.tsx | 1 + .../components/matrix_histogram/index.tsx | 5 +- .../components/matrix_histogram/types.ts | 2 +- .../navigation/breadcrumbs/index.test.ts | 31 +- .../common/components/navigation/helpers.ts | 11 +- .../components/navigation/index.test.tsx | 3 + .../common/components/navigation/index.tsx | 10 +- .../navigation/tab_navigation/index.test.tsx | 2 + .../navigation/tab_navigation/index.tsx | 13 +- .../navigation/tab_navigation/types.ts | 2 + .../common/components/navigation/types.ts | 2 + .../components/sourcerer/index.test.tsx | 192 +- .../common/components/sourcerer/index.tsx | 301 +- .../common/components/sourcerer/selectors.tsx | 35 + .../components/sourcerer/translations.ts | 37 +- .../public/common/components/top_n/helpers.ts | 8 +- .../common/components/top_n/index.test.tsx | 93 +- .../public/common/components/top_n/index.tsx | 7 +- .../common/components/top_n/top_n.test.tsx | 109 +- .../public/common/components/top_n/top_n.tsx | 16 +- .../common/components/url_state/constants.ts | 1 + .../common/components/url_state/helpers.ts | 20 + .../components/url_state/index.test.tsx | 2 +- .../url_state/index_mocked.test.tsx | 45 +- .../url_state/initialize_redux_by_url.tsx | 20 +- .../components/url_state/test_dependencies.ts | 1 + .../common/components/url_state/types.ts | 9 + .../anomalies_query_tab_body/index.tsx | 2 + .../anomalies_query_tab_body/types.ts | 1 + .../events/last_event_time/index.ts | 23 +- .../containers/matrix_histogram/index.ts | 22 +- .../common/containers/query_template.tsx | 1 + .../containers/source/index.gql_query.ts | 31 - .../common/containers/source/index.test.tsx | 109 - .../public/common/containers/source/index.tsx | 329 +- .../public/common/containers/source/mock.ts | 625 +- .../common/containers/source/translations.ts | 21 + .../common/containers/sourcerer/constants.ts | 24 +- .../containers/sourcerer/format.test.tsx | 23 - .../common/containers/sourcerer/format.ts | 96 - .../containers/sourcerer/index.test.tsx | 368 +- .../common/containers/sourcerer/index.tsx | 450 +- .../common/containers/sourcerer/mocks.ts | 98 +- .../common/lib/kibana/__mocks__/index.ts | 20 +- .../public/common/mock/global_state.ts | 27 + .../public/common/mock/index_pattern.ts | 6 +- .../public/common/mock/timeline_results.ts | 2 + .../public/common/store/actions.ts | 1 + .../public/common/store/model.ts | 1 + .../public/common/store/reducer.ts | 14 +- .../public/common/store/selectors.ts | 1 + .../public/common/store/sourcerer/actions.ts | 36 + .../public/common/store/sourcerer/index.ts | 12 + .../public/common/store/sourcerer/model.ts | 86 + .../public/common/store/sourcerer/reducer.ts | 93 + .../common/store/sourcerer/selectors.ts | 91 + .../public/common/store/types.ts | 2 + .../components/alerts_table/actions.test.tsx | 1 + .../components/alerts_table/actions.tsx | 2 + .../components/alerts_table/index.test.tsx | 1 - .../components/alerts_table/index.tsx | 27 +- .../timeline_actions/alert_context_menu.tsx | 42 +- .../investigate_in_timeline_action.tsx | 3 +- .../detection_engine_header_page/index.tsx | 2 +- .../rules/step_about_rule/index.test.tsx | 10 +- .../rules/step_about_rule/index.tsx | 8 +- .../rules/step_define_rule/index.tsx | 7 +- .../detections/components/user_info/index.tsx | 24 +- .../rules/fetch_index_patterns.test.tsx | 475 - .../rules/fetch_index_patterns.tsx | 132 - .../detection_engine/rules/index.ts | 1 - .../detection_engine.test.tsx | 6 +- .../detection_engine/detection_engine.tsx | 9 +- .../rules/details/index.test.tsx | 6 +- .../detection_engine/rules/details/index.tsx | 12 +- .../public/graphql/introspection.json | 326 +- .../security_solution/public/graphql/types.ts | 125 +- .../first_last_seen_host/index.test.tsx | 49 +- .../components/first_last_seen_host/index.tsx | 75 +- .../__snapshots__/index.test.tsx.snap | 91 - .../components/hosts_table/index.test.tsx | 3 - .../hosts/components/hosts_table/index.tsx | 3 - .../kpi_hosts/authentications/index.tsx | 2 + .../components/kpi_hosts/hosts/index.tsx | 2 + .../hosts/components/kpi_hosts/index.tsx | 9 +- .../hosts/components/kpi_hosts/types.ts | 1 + .../components/kpi_hosts/unique_ips/index.tsx | 2 + .../containers/authentications/index.tsx | 12 +- .../hosts/containers/hosts/details/_index.tsx | 18 +- .../hosts/containers/hosts/details/index.tsx | 11 +- .../hosts/first_last_seen/index.tsx | 18 +- .../public/hosts/containers/hosts/index.tsx | 12 +- .../containers/kpi_host_details/index.tsx | 16 +- .../kpi_hosts/authentications/index.tsx | 12 +- .../containers/kpi_hosts/hosts/index.tsx | 12 +- .../containers/kpi_hosts/unique_ips/index.tsx | 12 +- .../containers/uncommon_processes/index.tsx | 12 +- .../hosts/pages/details/details_tabs.test.tsx | 1 + .../hosts/pages/details/details_tabs.tsx | 8 +- .../public/hosts/pages/details/index.tsx | 16 +- .../public/hosts/pages/details/types.ts | 1 + .../public/hosts/pages/hosts.test.tsx | 14 +- .../public/hosts/pages/hosts.tsx | 15 +- .../public/hosts/pages/hosts_tabs.tsx | 12 +- .../authentications_query_tab_body.tsx | 12 +- .../navigation/events_query_tab_body.tsx | 4 + .../pages/navigation/hosts_query_tab_body.tsx | 5 +- .../public/hosts/pages/navigation/types.ts | 4 +- .../uncommon_process_query_tab_body.tsx | 11 +- .../public/hosts/pages/types.ts | 3 +- .../components/administration_list_page.tsx | 7 +- .../pages/policy/view/policy_details.tsx | 1 + .../trusted_apps_page.test.tsx.snap | 66 + .../view/trusted_apps_page.test.tsx | 1 + .../__snapshots__/index.test.tsx.snap | 1 + .../components/kpi_network/dns/index.tsx | 2 + .../components/kpi_network/index.test.tsx | 5 +- .../network/components/kpi_network/index.tsx | 7 +- .../kpi_network/network_events/index.tsx | 2 + .../kpi_network/tls_handshakes/index.tsx | 2 + .../network/components/kpi_network/types.ts | 1 + .../kpi_network/unique_flows/index.tsx | 2 + .../kpi_network/unique_private_ips/index.tsx | 2 + .../network/containers/details/index.tsx | 12 +- .../containers/kpi_network/dns/index.tsx | 12 +- .../kpi_network/network_events/index.tsx | 12 +- .../kpi_network/tls_handshakes/index.tsx | 12 +- .../kpi_network/unique_flows/index.tsx | 12 +- .../kpi_network/unique_private_ips/index.tsx | 12 +- .../network/containers/network_dns/index.tsx | 12 +- .../network/containers/network_http/index.tsx | 12 +- .../network_top_countries/index.tsx | 12 +- .../containers/network_top_n_flow/index.tsx | 12 +- .../public/network/containers/tls/index.tsx | 12 +- .../network/pages/details/index.test.tsx | 8 +- .../public/network/pages/details/index.tsx | 22 +- .../details/network_http_query_table.tsx | 1 + .../network_top_countries_query_table.tsx | 1 + .../network_top_n_flow_query_table.tsx | 2 + .../network/pages/details/tls_query_table.tsx | 1 + .../public/network/pages/details/types.ts | 1 + .../navigation/countries_query_tab_body.tsx | 2 + .../pages/navigation/dns_query_tab_body.tsx | 3 + .../pages/navigation/http_query_tab_body.tsx | 2 + .../pages/navigation/ips_query_tab_body.tsx | 2 + .../pages/navigation/network_routes.tsx | 2 + .../pages/navigation/tls_query_tab_body.tsx | 2 + .../public/network/pages/navigation/types.ts | 2 + .../public/network/pages/network.test.tsx | 31 +- .../public/network/pages/network.tsx | 20 +- .../alerts_by_category/index.test.tsx | 28 +- .../components/alerts_by_category/index.tsx | 3 + .../components/event_counts/index.test.tsx | 18 +- .../components/event_counts/index.tsx | 4 + .../components/events_by_dataset/index.tsx | 6 +- .../__snapshots__/index.test.tsx.snap | 2 + .../components/host_overview/index.test.tsx | 2 + .../components/host_overview/index.tsx | 12 +- .../components/overview_host/index.test.tsx | 13 +- .../components/overview_host/index.tsx | 9 +- .../overview_network/index.test.tsx | 16 +- .../components/overview_network/index.tsx | 3 + .../recent_cases/no_cases/index.test.tsx | 2 +- .../containers/overview_host/index.tsx | 23 +- .../containers/overview_network/index.tsx | 12 +- .../public/overview/pages/overview.test.tsx | 184 +- .../public/overview/pages/overview.tsx | 21 +- .../public/overview/pages/summary.tsx | 6 +- .../security_solution/public/plugin.tsx | 56 +- .../components/flyout/button/index.tsx | 5 +- .../components/manage_timeline/index.test.tsx | 54 +- .../components/manage_timeline/index.tsx | 39 - .../components/open_timeline/helpers.test.ts | 6 + .../components/open_timeline/helpers.ts | 9 + .../components/open_timeline/index.tsx | 28 +- .../__snapshots__/timeline.test.tsx.snap | 14 +- .../components/timeline/body/helpers.tsx | 9 +- .../components/timeline/body/index.tsx | 8 +- .../timeline/data_providers/helpers.test.tsx | 6 +- .../timeline/data_providers/helpers.tsx | 8 +- .../components/timeline/index.test.tsx | 153 +- .../timelines/components/timeline/index.tsx | 46 +- .../properties/use_create_timeline.test.tsx | 9 +- .../properties/use_create_timeline.tsx | 28 +- .../timeline/search_or_filter/index.tsx | 38 +- .../timeline/search_or_filter/pick_events.tsx | 358 +- .../search_or_filter/search_or_filter.tsx | 14 +- .../timeline/search_or_filter/selectors.tsx | 43 + .../timeline/search_or_filter/translations.ts | 60 +- .../timelines/components/timeline/styles.tsx | 4 +- .../components/timeline/timeline.test.tsx | 33 +- .../components/timeline/timeline.tsx | 46 +- .../timelines/containers/details/index.tsx | 7 +- .../public/timelines/containers/index.tsx | 98 +- .../containers/one/index.gql_query.ts | 1 + .../timelines/containers/persist.gql_query.ts | 1 + .../timelines/pages/timelines_page.test.tsx | 6 +- .../public/timelines/pages/timelines_page.tsx | 10 +- .../timelines/store/timeline/actions.ts | 16 +- .../timelines/store/timeline/defaults.ts | 1 + .../timelines/store/timeline/epic.test.ts | 2 + .../public/timelines/store/timeline/epic.ts | 3 + .../timeline/epic_local_storage.test.tsx | 6 +- .../timelines/store/timeline/helpers.ts | 8 +- .../public/timelines/store/timeline/model.ts | 7 +- .../timelines/store/timeline/reducer.test.ts | 1537 +- .../timelines/store/timeline/reducer.ts | 13 + .../scripts/beat_docs/build.js | 233 + .../graphql/source_status/schema.gql.ts | 2 +- .../server/graphql/timeline/schema.gql.ts | 2 + .../security_solution/server/graphql/types.ts | 265 +- .../server/lib/compose/kibana.ts | 2 +- .../lib/events/elasticsearch_adapter.ts | 3 +- .../lib/index_fields/elasticsearch_adapter.ts | 165 +- .../server/lib/index_fields/index.ts | 5 +- .../server/lib/index_fields/types.ts | 3 +- .../lib/timeline/saved_object_mappings.ts | 3 + .../security_solution/server/plugin.ts | 7 + .../index_fields/index.test.ts} | 230 +- .../search_strategy/index_fields/index.ts | 224 + .../search_strategy/index_fields/mock.ts | 113 + .../timeline/factory/events/all/helpers.ts | 1 + .../factory/events/details/helpers.ts | 3 +- .../utils/beat_schema/8.0.0/auditbeat.ts | 7902 ---- .../server/utils/beat_schema/8.0.0/ecs.ts | 5675 --- .../utils/beat_schema/8.0.0/filebeat.ts | 21243 --------- .../server/utils/beat_schema/8.0.0/index.ts | 37 - .../utils/beat_schema/8.0.0/packetbeat.ts | 8556 ---- .../utils/beat_schema/8.0.0/winlogbeat.ts | 2844 -- .../server/utils/beat_schema/fields.ts | 36118 ++++++++++++++++ .../server/utils/beat_schema/index.test.ts | 397 - .../server/utils/beat_schema/index.ts | 129 - .../server/utils/beat_schema/type.ts | 73 - .../apis/security_solution/sources.ts | 147 +- 272 files changed, 41559 insertions(+), 52569 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getidswithtitle.md create mode 100644 x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/sourcerer/selectors.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/containers/source/index.gql_query.ts delete mode 100644 x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/containers/source/translations.ts delete mode 100644 x-pack/plugins/security_solution/public/common/containers/sourcerer/format.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/containers/sourcerer/format.ts create mode 100644 x-pack/plugins/security_solution/public/common/store/sourcerer/actions.ts create mode 100644 x-pack/plugins/security_solution/public/common/store/sourcerer/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts create mode 100644 x-pack/plugins/security_solution/public/common/store/sourcerer/reducer.ts create mode 100644 x-pack/plugins/security_solution/public/common/store/sourcerer/selectors.ts delete mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.tsx create mode 100644 x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/selectors.tsx create mode 100644 x-pack/plugins/security_solution/scripts/beat_docs/build.js rename x-pack/plugins/security_solution/server/{lib/index_fields/elasticsearch_adapter.test.ts => search_strategy/index_fields/index.test.ts} (65%) create mode 100644 x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/index_fields/mock.ts delete mode 100644 x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/auditbeat.ts delete mode 100644 x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/ecs.ts delete mode 100644 x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/filebeat.ts delete mode 100644 x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/index.ts delete mode 100644 x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/packetbeat.ts delete mode 100644 x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/winlogbeat.ts create mode 100644 x-pack/plugins/security_solution/server/utils/beat_schema/fields.ts delete mode 100644 x-pack/plugins/security_solution/server/utils/beat_schema/index.test.ts delete mode 100644 x-pack/plugins/security_solution/server/utils/beat_schema/index.ts delete mode 100644 x-pack/plugins/security_solution/server/utils/beat_schema/type.ts diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getidswithtitle.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getidswithtitle.md new file mode 100644 index 00000000000000..7d29ced66afa84 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getidswithtitle.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [getIdsWithTitle](./kibana-plugin-plugins-data-public.indexpatternsservice.getidswithtitle.md) + +## IndexPatternsService.getIdsWithTitle property + +Get list of index pattern ids with titles + +Signature: + +```typescript +getIdsWithTitle: (refresh?: boolean) => Promise>; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md index 0022bff34a8e76..af087344268d73 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md @@ -29,6 +29,7 @@ export declare class IndexPatternsService | [getFieldsForIndexPattern](./kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforindexpattern.md) | | (indexPattern: IndexPattern | IndexPatternSpec, options?: GetFieldsOptions) => Promise<any> | Get field list by providing an index patttern (or spec) | | [getFieldsForWildcard](./kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforwildcard.md) | | (options?: GetFieldsOptions) => Promise<any> | Get field list by providing { pattern } | | [getIds](./kibana-plugin-plugins-data-public.indexpatternsservice.getids.md) | | (refresh?: boolean) => Promise<string[]> | Get list of index pattern ids | +| [getIdsWithTitle](./kibana-plugin-plugins-data-public.indexpatternsservice.getidswithtitle.md) | | (refresh?: boolean) => Promise<Array<{
    id: string;
    title: string;
    }>> | Get list of index pattern ids with titles | | [getTitles](./kibana-plugin-plugins-data-public.indexpatternsservice.gettitles.md) | | (refresh?: boolean) => Promise<string[]> | Get list of index pattern titles | | [refreshFields](./kibana-plugin-plugins-data-public.indexpatternsservice.refreshfields.md) | | (indexPattern: IndexPattern) => Promise<void> | Refresh field list for a given index pattern | | [savedObjectToSpec](./kibana-plugin-plugins-data-public.indexpatternsservice.savedobjecttospec.md) | | (savedObject: SavedObject<IndexPatternAttributes>) => IndexPatternSpec | Converts index pattern saved object to index pattern spec | diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index c56954ba6a29b3..eef8ef10ea7546 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -133,6 +133,25 @@ export class IndexPatternsService { return this.savedObjectsCache.map((obj) => obj?.attributes?.title); }; + /** + * Get list of index pattern ids with titles + * @param refresh Force refresh of index pattern list + */ + getIdsWithTitle = async ( + refresh: boolean = false + ): Promise> => { + if (!this.savedObjectsCache || refresh) { + await this.refreshSavedObjectsCache(); + } + if (!this.savedObjectsCache) { + return []; + } + return this.savedObjectsCache.map((obj) => ({ + id: obj?.id, + title: obj?.attributes?.title, + })); + }; + /** * Clear index pattern list cache * @param id optionally clear a single id diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 5919c1e294b2f2..ed58ee840a8f83 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1381,6 +1381,10 @@ export class IndexPatternsService { // Warning: (ae-forgotten-export) The symbol "GetFieldsOptions" needs to be exported by the entry point index.d.ts getFieldsForWildcard: (options?: GetFieldsOptions) => Promise; getIds: (refresh?: boolean) => Promise; + getIdsWithTitle: (refresh?: boolean) => Promise>; getTitles: (refresh?: boolean) => Promise; refreshFields: (indexPattern: IndexPattern) => Promise; savedObjectToSpec: (savedObject: SavedObject) => IndexPatternSpec; diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 9321aa769423f3..a93d2817fbbb3e 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -11,7 +11,6 @@ export const APP_ICON = 'securityAnalyticsApp'; export const APP_ICON_SOLUTION = 'logoSecurity'; export const APP_PATH = `/app/security`; export const ADD_DATA_PATH = `/app/home#/tutorial_directory/security`; -export const ADD_INDEX_PATH = `/app/management/kibana/indexPatterns/create`; export const DEFAULT_BYTES_FORMAT = 'format:bytes:defaultPattern'; export const DEFAULT_DATE_FORMAT = 'dateFormat'; export const DEFAULT_DATE_FORMAT_TZ = 'dateFormat:tz'; @@ -58,6 +57,8 @@ export const APP_TIMELINES_PATH = `${APP_PATH}/timelines`; export const APP_CASES_PATH = `${APP_PATH}/cases`; export const APP_MANAGEMENT_PATH = `${APP_PATH}/administration`; +export const DETECTIONS_SUB_PLUGIN_ID = `${APP_ID}:${SecurityPageName.detections}`; + /** The comma-delimited list of Elasticsearch indices from which the SIEM app collects events */ export const DEFAULT_INDEX_PATTERN = [ 'apm-*-transaction*', diff --git a/x-pack/plugins/security_solution/common/search_strategy/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/common/index.ts index 48437e12f75a50..0c1f13dac2e697 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/common/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/common/index.ts @@ -71,7 +71,7 @@ export interface PaginationInputPaginated { export interface DocValueFields { field: string; - format: string; + format?: string | null; } export interface Explanation { diff --git a/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts b/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts new file mode 100644 index 00000000000000..259a767f8cf704 --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts @@ -0,0 +1,81 @@ +/* + * 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 { IIndexPattern } from 'src/plugins/data/public'; +import { + IEsSearchRequest, + IEsSearchResponse, + IFieldSubType, +} from '../../../../../../src/plugins/data/common'; +import { DocValueFields, Maybe } from '../common'; + +export type BeatFieldsFactoryQueryType = 'beatFields'; + +interface FieldInfo { + category: string; + description?: string; + example?: string | number; + format?: string; + name: string; + type?: string; +} + +export interface IndexField { + /** Where the field belong */ + category: string; + /** Example of field's value */ + example?: Maybe; + /** whether the field's belong to an alias index */ + indexes: Array>; + /** The name of the field */ + name: string; + /** The type of the field's values as recognized by Kibana */ + type: string; + /** Whether the field's values can be efficiently searched for */ + searchable: boolean; + /** Whether the field's values can be aggregated */ + aggregatable: boolean; + /** Description of the field */ + description?: Maybe; + format?: Maybe; + /** the elastic type as mapped in the index */ + esTypes?: string[]; + subType?: IFieldSubType; + readFromDocValues: boolean; +} + +export type BeatFields = Record; + +export interface IndexFieldsStrategyRequest extends IEsSearchRequest { + indices: string[]; + onlyCheckIfIndicesExist: boolean; +} + +export interface IndexFieldsStrategyResponse extends IEsSearchResponse { + indexFields: IndexField[]; + indicesExist: string[]; +} + +export interface BrowserField { + aggregatable: boolean; + category: string; + description: string | null; + example: string | number | null; + fields: Readonly>>; + format: string; + indexes: string[]; + name: string; + searchable: boolean; + type: string; +} + +export type BrowserFields = Readonly>>; + +export const EMPTY_BROWSER_FIELDS = {}; +export const EMPTY_DOCVALUE_FIELD: DocValueFields[] = []; +export const EMPTY_INDEX_PATTERN: IIndexPattern = { + fields: [], + title: '', +}; diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/details/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/details/index.ts index 6f9192be401500..9fa7f96599debb 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/details/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/details/index.ts @@ -22,7 +22,6 @@ export interface TimelineEventsDetailsStrategyResponse extends IEsSearchResponse export interface TimelineEventsDetailsRequestOptions extends Partial { - defaultIndex: string[]; indexName: string; eventId: string; } diff --git a/x-pack/plugins/security_solution/common/types/timeline/index.ts b/x-pack/plugins/security_solution/common/types/timeline/index.ts index 84a007e322f11b..3888d37a547f7a 100644 --- a/x-pack/plugins/security_solution/common/types/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/types/timeline/index.ts @@ -239,6 +239,7 @@ export const SavedTimelineRuntimeType = runtimeTypes.partial({ excludedRowRendererIds: unionWithNullType(runtimeTypes.array(RowRendererIdRuntimeType)), favorite: unionWithNullType(runtimeTypes.array(SavedFavoriteRuntimeType)), filters: unionWithNullType(runtimeTypes.array(SavedFilterRuntimeType)), + indexNames: unionWithNullType(runtimeTypes.array(runtimeTypes.string)), kqlMode: unionWithNullType(runtimeTypes.string), kqlQuery: unionWithNullType(SavedFilterQueryQueryRuntimeType), title: unionWithNullType(runtimeTypes.string), @@ -398,3 +399,5 @@ export const importTimelineResultSchema = runtimeTypes.exact( ); export type ImportTimelineResultSchema = runtimeTypes.TypeOf; + +export type TimelineEventsType = 'all' | 'raw' | 'alert' | 'signal' | 'custom'; diff --git a/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts b/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts index 0b302efd655a8c..06a8d3a79c3cdb 100644 --- a/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts @@ -94,7 +94,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkSingleIpNullKqlQuery); cy.url().should( 'include', - '/app/security/network/ip/127.0.0.1/source?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' + '/app/security/network/ip/127.0.0.1/source?sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' ); }); @@ -102,7 +102,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkSingleIpKqlQuery); cy.url().should( 'include', - '/app/security/network/ip/127.0.0.1/source?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' + '/app/security/network/ip/127.0.0.1/source?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' ); }); @@ -110,7 +110,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkMultipleIpNullKqlQuery); cy.url().should( 'include', - 'app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27))' + 'app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%27)&sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27))' ); }); @@ -118,7 +118,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkMultipleIpKqlQuery); cy.url().should( 'include', - '/app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' + '/app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' ); }); @@ -126,7 +126,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkNullKqlQuery); cy.url().should( 'include', - '/app/security/network/flows?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' + '/app/security/network/flows?sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' ); }); @@ -134,7 +134,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkKqlQuery); cy.url().should( 'include', - '/app/security/network/flows?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' + '/app/security/network/flows?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' ); }); @@ -142,7 +142,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostNullKqlQuery); cy.url().should( 'include', - '/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/siem-windows/anomalies?sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' ); }); @@ -150,7 +150,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostKqlQueryVariable); cy.url().should( 'include', - '/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/siem-windows/anomalies?sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' ); }); @@ -158,7 +158,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostKqlQuery); cy.url().should( 'include', - '/app/security/hosts/siem-windows/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/siem-windows/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' ); }); @@ -166,7 +166,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostMultiHostNullKqlQuery); cy.url().should( 'include', - '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%27)&sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' ); }); @@ -174,7 +174,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostMultiHostKqlQuery); cy.url().should( 'include', - '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' ); }); @@ -182,7 +182,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostVariableHostNullKqlQuery); cy.url().should( 'include', - '/app/security/hosts/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/anomalies?sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' ); }); @@ -190,7 +190,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostVariableHostKqlQuery); cy.url().should( 'include', - '/app/security/hosts/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&sourcerer=(default:!(%27auditbeat-*%27))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' ); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts b/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts index 5dc3182cd9f836..fdccf164c74658 100644 --- a/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts @@ -35,6 +35,7 @@ describe('Pagination', () => { .then((processNameFirstPage) => { goToThirdPage(); waitForUncommonProcessesToBeLoaded(); + cy.wait(1500); cy.get(PROCESS_NAME_FIELD) .first() .invoke('text') diff --git a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts index 04aecfab4561fe..2588c580dedd34 100644 --- a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts @@ -166,7 +166,7 @@ describe.skip('url state', () => { cy.get(NETWORK).should( 'have.attr', 'href', - `/app/security/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')))` + `/app/security/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')))` ); }); @@ -179,12 +179,12 @@ describe.skip('url state', () => { cy.get(HOSTS).should( 'have.attr', 'href', - `/app/security/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` + `/app/security/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` ); cy.get(NETWORK).should( 'have.attr', 'href', - `/app/security/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` + `/app/security/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` ); cy.get(HOSTS_NAMES).first().invoke('text').should('eq', 'siem-kibana'); @@ -195,21 +195,21 @@ describe.skip('url state', () => { cy.get(ANOMALIES_TAB).should( 'have.attr', 'href', - "/app/security/hosts/siem-kibana/anomalies?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))" + "/app/security/hosts/siem-kibana/anomalies?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&sourcerer=(default:!('auditbeat-*'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))" ); cy.get(BREADCRUMBS) .eq(1) .should( 'have.attr', 'href', - `/app/security/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` + `/app/security/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` ); cy.get(BREADCRUMBS) .eq(2) .should( 'have.attr', 'href', - `/app/security/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` + `/app/security/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` ); }); diff --git a/x-pack/plugins/security_solution/package.json b/x-pack/plugins/security_solution/package.json index 6982c200a5afdc..6d79557fdaa28a 100644 --- a/x-pack/plugins/security_solution/package.json +++ b/x-pack/plugins/security_solution/package.json @@ -6,6 +6,7 @@ "license": "Elastic-License", "scripts": { "extract-mitre-attacks": "node scripts/extract_tactics_techniques_mitre.js && node ../../../scripts/eslint ./public/pages/detection_engine/mitre/mitre_tactics_techniques.ts --fix", + "build-beat-doc": "node scripts/beat_docs/build.js && node ../../../scripts/eslint ./server/utils/beat_schema/fields.ts --fix", "build-graphql-types": "node scripts/generate_types_from_graphql.js", "cypress:open": "cypress open --config-file ./cypress/cypress.json", "cypress:open-as-ci": "node ../../../scripts/functional_tests --config ../../test/security_solution_cypress/visual_config.ts", diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index b4e9ba3dd7a716..54b02c374e43fa 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -28,8 +28,6 @@ import { ApolloClientContext } from '../common/utils/apollo_context'; import { ManageGlobalTimeline } from '../timelines/components/manage_timeline'; import { StartServices } from '../types'; import { PageRouter } from './routes'; -import { ManageSource } from '../common/containers/sourcerer'; - interface StartAppComponent extends AppFrontendLibs { children: React.ReactNode; history: History; @@ -56,15 +54,13 @@ const StartAppComponent: FC = ({ children, apolloClient, hist - - - - - {children} - - - - + + + + {children} + + + diff --git a/x-pack/plugins/security_solution/public/app/home/index.tsx b/x-pack/plugins/security_solution/public/app/home/index.tsx index b48ae4e6e2d75c..e0dea199e78ff4 100644 --- a/x-pack/plugins/security_solution/public/app/home/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useMemo } from 'react'; +import React, { useRef } from 'react'; import styled from 'styled-components'; import { TimelineId } from '../../../common/types/timeline'; @@ -14,11 +14,12 @@ import { HeaderGlobal } from '../../common/components/header_global'; import { HelpMenu } from '../../common/components/help_menu'; import { AutoSaveWarningMsg } from '../../timelines/components/timeline/auto_save_warning'; import { UseUrlState } from '../../common/components/url_state'; -import { useWithSource } from '../../common/containers/source'; import { useShowTimeline } from '../../common/utils/timeline/use_show_timeline'; import { navTabs } from './home_navigations'; -import { useSignalIndex } from '../../detections/containers/detection_engine/alerts/use_signal_index'; -import { useUserInfo } from '../../detections/components/user_info'; +import { useInitSourcerer, useSourcererScope } from '../../common/containers/sourcerer'; +import { useKibana } from '../../common/lib/kibana'; +import { DETECTIONS_SUB_PLUGIN_ID } from '../../../common/constants'; +import { SourcererScopeName } from '../../common/store/sourcerer/model'; const SecuritySolutionAppWrapper = styled.div` display: flex; @@ -42,20 +43,21 @@ interface HomePageProps { } const HomePageComponent: React.FC = ({ children }) => { - const { signalIndexExists, signalIndexName } = useSignalIndex(); + const { application } = useKibana().services; + const subPluginId = useRef(''); - const indexToAdd = useMemo(() => { - if (signalIndexExists && signalIndexName != null) { - return [signalIndexName]; - } - return null; - }, [signalIndexExists, signalIndexName]); + application.currentAppId$.subscribe((appId) => { + subPluginId.current = appId ?? ''; + }); + useInitSourcerer( + subPluginId.current === DETECTIONS_SUB_PLUGIN_ID + ? SourcererScopeName.detections + : SourcererScopeName.default + ); const [showTimeline] = useShowTimeline(); - const { browserFields, indexPattern, indicesExist } = useWithSource('default', indexToAdd); - // side effect: this will attempt to create the signals index if it doesn't exist - useUserInfo(); + const { browserFields, indexPattern, indicesExist } = useSourcererScope(); return ( diff --git a/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx index 0ac6093f2ee040..4f7b17a730b6ab 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx @@ -9,7 +9,9 @@ import React from 'react'; import { HeaderPage, HeaderPageProps } from '../../../common/components/header_page'; import * as i18n from './translations'; -const CaseHeaderPageComponent: React.FC = (props) => ; +const CaseHeaderPageComponent: React.FC = (props) => ( + +); CaseHeaderPageComponent.defaultProps = { badgeOptions: { diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx index b23169af6ceb30..ad113d3e7e7372 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx @@ -294,6 +294,7 @@ export const CaseComponent = React.memo( = ({ defaultModel={alertsDefaultModel} end={endDate} id={timelineId} + scopeId={SourcererScopeName.default} start={startDate} /> ); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx index d522e372d7734b..0dcd29a2d965b7 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx @@ -24,6 +24,7 @@ const AlertsViewComponent: React.FC = ({ deleteQuery, endDate, filterQuery, + indexNames, pageFilters, setQuery, startDate, @@ -62,6 +63,7 @@ const AlertsViewComponent: React.FC = ({ endDate={endDate} filterQuery={filterQuery} id={ID} + indexNames={indexNames} setQuery={setQuery} startDate={startDate} {...alertsHistogramConfigs} diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts index b2637eeb2c65eb..280b9111042d0e 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts @@ -15,7 +15,7 @@ type CommonQueryProps = HostsComponentsQueryProps | NetworkComponentQueryProps; export interface AlertsComponentsProps extends Pick< CommonQueryProps, - 'deleteQuery' | 'endDate' | 'filterQuery' | 'skip' | 'setQuery' | 'startDate' + 'deleteQuery' | 'endDate' | 'filterQuery' | 'indexNames' | 'skip' | 'setQuery' | 'startDate' > { timelineId: TimelineIdLiteral; pageFilters: Filter[]; diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.test.tsx index 9e8bde8d9ff92c..eaaba90b356344 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.test.tsx @@ -6,9 +6,8 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; -import { MockedProvider } from 'react-apollo/test-utils'; -import { mockBrowserFields, mocksSource } from '../../containers/source/mock'; +import { mockBrowserFields } from '../../containers/source/mock'; import { TestProviders } from '../../mock'; import { DragDropContextWrapper } from './drag_drop_context_wrapper'; @@ -20,11 +19,9 @@ describe('DragDropContextWrapper', () => { const wrapper = shallow( - - - {message} - - + + {message} + ); expect(wrapper.find('DragDropContextWrapper')).toMatchSnapshot(); @@ -35,11 +32,9 @@ describe('DragDropContextWrapper', () => { const wrapper = mount( - - - {message} - - + + {message} + ); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx index ebfa9ac22bdc71..46e7298677f493 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx @@ -6,11 +6,10 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { MockedProvider } from 'react-apollo/test-utils'; import { DraggableStateSnapshot, DraggingStyle } from 'react-beautiful-dnd'; import '../../mock/match_media'; -import { mockBrowserFields, mocksSource } from '../../containers/source/mock'; +import { mockBrowserFields } from '../../containers/source/mock'; import { TestProviders } from '../../mock'; import { mockDataProviders } from '../../../timelines/components/timeline/data_providers/mock/mock_data_providers'; import { DragDropContextWrapper } from './drag_drop_context_wrapper'; @@ -30,11 +29,9 @@ describe('DraggableWrapper', () => { test('it renders against the snapshot', () => { const wrapper = shallow( - - - message} /> - - + + message} /> + ); @@ -44,11 +41,9 @@ describe('DraggableWrapper', () => { test('it renders the children passed to the render prop', () => { const wrapper = mount( - - - message} /> - - + + message} /> + ); @@ -58,11 +53,9 @@ describe('DraggableWrapper', () => { test('it does NOT render hover actions when the mouse is NOT over the draggable wrapper', () => { const wrapper = mount( - - - message} /> - - + + message} /> + ); @@ -72,11 +65,9 @@ describe('DraggableWrapper', () => { test('it renders hover actions when the mouse is over the text of draggable wrapper', () => { const wrapper = mount( - - - message} /> - - + + message} /> + ); @@ -92,11 +83,9 @@ describe('DraggableWrapper', () => { test('it applies text truncation styling when truncate IS specified (implicit: and the user is not dragging)', () => { const wrapper = mount( - - - message} truncate /> - - + + message} truncate /> + ); @@ -108,11 +97,9 @@ describe('DraggableWrapper', () => { test('it does NOT apply text truncation styling when truncate is NOT specified', () => { const wrapper = mount( - - - message} /> - - + + message} /> + ); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx index b53da42da55f86..8aa926a36988bd 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx @@ -8,14 +8,13 @@ import { mount, ReactWrapper } from 'enzyme'; import React from 'react'; import { coreMock } from '../../../../../../../src/core/public/mocks'; -import { useWithSource } from '../../containers/source'; import { mockBrowserFields } from '../../containers/source/mock'; import '../../mock/match_media'; import { useKibana } from '../../lib/kibana'; import { TestProviders } from '../../mock'; import { FilterManager } from '../../../../../../../src/plugins/data/public'; import { useAddToTimeline } from '../../hooks/use_add_to_timeline'; - +import { useSourcererScope } from '../../containers/sourcerer'; import { DraggableWrapperHoverContent } from './draggable_wrapper_hover_content'; import { ManageGlobalTimeline, @@ -26,12 +25,12 @@ import { TimelineId } from '../../../../common/types/timeline'; jest.mock('../link_to'); jest.mock('../../lib/kibana'); -jest.mock('../../containers/source', () => { - const original = jest.requireActual('../../containers/source'); +jest.mock('../../containers/sourcerer', () => { + const original = jest.requireActual('../../containers/sourcerer'); return { ...original, - useWithSource: jest.fn(), + useSourcererScope: jest.fn(), }; }); @@ -79,8 +78,10 @@ describe('DraggableWrapperHoverContent', () => { beforeAll(() => { // our mock implementation of the useAddToTimeline hook returns a mock startDragToTimeline function: (useAddToTimeline as jest.Mock).mockReturnValue(jest.fn()); - (useWithSource as jest.Mock).mockReturnValue({ + (useSourcererScope as jest.Mock).mockReturnValue({ browserFields: mockBrowserFields, + selectedPatterns: [], + indexPattern: {}, }); }); @@ -203,7 +204,7 @@ describe('DraggableWrapperHoverContent', () => { wrapper = mount( ); @@ -311,7 +312,7 @@ describe('DraggableWrapperHoverContent', () => { {...{ ...defaultProps, onFilterAdded, - timelineId: 'not-active-timeline', + timelineId: TimelineId.test, value: '', }} /> @@ -606,9 +607,7 @@ describe('DraggableWrapperHoverContent', () => { test('filter manager, not active timeline', () => { mount( - + ); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx index a951bfa98d64b5..8c68551ddd981e 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx @@ -8,7 +8,7 @@ import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import React, { useCallback, useMemo, useState, useEffect } from 'react'; import { DraggableId } from 'react-beautiful-dnd'; -import { getAllFieldsByName, useWithSource } from '../../containers/source'; +import { getAllFieldsByName } from '../../containers/source'; import { useAddToTimeline } from '../../hooks/use_add_to_timeline'; import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard'; import { useKibana } from '../../lib/kibana'; @@ -20,6 +20,8 @@ import * as i18n from './translations'; import { useManageTimeline } from '../../../timelines/components/manage_timeline'; import { TimelineId } from '../../../../common/types/timeline'; import { SELECTOR_TIMELINE_BODY_CLASS_NAME } from '../../../timelines/components/timeline/styles'; +import { SourcererScopeName } from '../../store/sourcerer/model'; +import { useSourcererScope } from '../../containers/sourcerer'; interface Props { closePopOver?: () => void; @@ -49,7 +51,7 @@ const DraggableWrapperHoverContentComponent: React.FC = ({ const filterManagerBackup = useMemo(() => kibana.services.data.query.filterManager, [ kibana.services.data.query.filterManager, ]); - const { getManageTimelineById, getTimelineFilterManager } = useManageTimeline(); + const { getTimelineFilterManager } = useManageTimeline(); const filterManager = useMemo( () => @@ -65,13 +67,16 @@ const DraggableWrapperHoverContentComponent: React.FC = ({ // this component is rendered in the context of the active timeline. This // behavior enables the 'All events' view by appending the alerts index // to the index pattern. - const { indexToAdd } = useMemo( - () => - timelineId === TimelineId.active - ? getManageTimelineById(TimelineId.active) - : { indexToAdd: null }, - [getManageTimelineById, timelineId] - ); + const activeScope: SourcererScopeName = + timelineId === TimelineId.active + ? SourcererScopeName.timeline + : timelineId != null && + [TimelineId.detectionsPage, TimelineId.detectionsRulesDetailsPage].includes( + timelineId as TimelineId + ) + ? SourcererScopeName.detections + : SourcererScopeName.default; + const { browserFields, indexPattern, selectedPatterns } = useSourcererScope(activeScope); const handleStartDragToTimeline = useCallback(() => { startDragToTimeline(); @@ -121,8 +126,6 @@ const DraggableWrapperHoverContentComponent: React.FC = ({ } }, [goGetTimelineId, timelineId]); - const { browserFields, indexPattern } = useWithSource('default', indexToAdd); - return ( <> {!showTopN && value != null && ( @@ -187,7 +190,7 @@ const DraggableWrapperHoverContentComponent: React.FC = ({ browserFields={browserFields} field={field} indexPattern={indexPattern} - indexToAdd={indexToAdd} + indexNames={selectedPatterns} onFilterAdded={onFilterAdded} timelineId={timelineId ?? undefined} toggleTopN={toggleTopN} diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/droppable_wrapper.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/droppable_wrapper.test.tsx index bd2f01721290fb..14d1c37efb8cfc 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/droppable_wrapper.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/droppable_wrapper.test.tsx @@ -6,9 +6,8 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { MockedProvider } from 'react-apollo/test-utils'; -import { mockBrowserFields, mocksSource } from '../../containers/source/mock'; +import { mockBrowserFields } from '../../containers/source/mock'; import { TestProviders } from '../../mock'; import { DragDropContextWrapper } from './drag_drop_context_wrapper'; @@ -24,11 +23,9 @@ describe('DroppableWrapper', () => { const wrapper = shallow( - - - {message} - - + + {message} + ); @@ -40,11 +37,9 @@ describe('DroppableWrapper', () => { const wrapper = mount( - - - {message} - - + + {message} + ); @@ -56,13 +51,11 @@ describe('DroppableWrapper', () => { const wrapper = mount( - - - null} droppableId="testing"> -
    {message}
    -
    -
    -
    + + null} droppableId="testing"> +
    {message}
    +
    +
    ); @@ -72,14 +65,12 @@ describe('DroppableWrapper', () => { test('it renders the render prop contents when a render prop is provided', () => { const wrapper = mount( - - -
    {`isDraggingOver is: ${isDraggingOver}`}
    } - droppableId="testing" - /> -
    -
    + +
    {`isDraggingOver is: ${isDraggingOver}`}
    } + droppableId="testing" + /> +
    ); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx index 037655f594241e..aac1f4f2687ebe 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx @@ -8,15 +8,13 @@ import React from 'react'; import useResizeObserver from 'use-resize-observer/polyfilled'; import '../../mock/match_media'; -import { mockIndexPattern, TestProviders } from '../../mock'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; +import { mockIndexNames, mockIndexPattern, TestProviders } from '../../mock'; import { mockEventViewerResponse } from './mock'; import { StatefulEventsViewer } from '.'; import { EventsViewer } from './events_viewer'; import { defaultHeaders } from './default_headers'; -import { useFetchIndexPatterns } from '../../../detections/containers/detection_engine/rules/fetch_index_patterns'; +import { useSourcererScope } from '../../containers/sourcerer'; import { mockBrowserFields, mockDocValueFields } from '../../containers/source/mock'; import { eventsDefaultModel } from './default_model'; import { useMountAppended } from '../../utils/use_mount_appended'; @@ -25,6 +23,7 @@ import { TimelineId } from '../../../../common/types/timeline'; import { KqlMode } from '../../../timelines/store/timeline/model'; import { SortDirection } from '../../../timelines/components/timeline/body/sort'; import { AlertsTableFilterGroup } from '../../../detections/components/alerts_table/alerts_filter_group'; +import { SourcererScopeName } from '../../store/sourcerer/model'; import { useTimelineEvents } from '../../../timelines/containers'; jest.mock('../../../timelines/containers', () => ({ @@ -33,8 +32,8 @@ jest.mock('../../../timelines/containers', () => ({ jest.mock('../../components/url_state/normalize_time_range.ts'); -const mockUseFetchIndexPatterns: jest.Mock = useFetchIndexPatterns as jest.Mock; -jest.mock('../../../detections/containers/detection_engine/rules/fetch_index_patterns'); +const mockUseSourcererScope: jest.Mock = useSourcererScope as jest.Mock; +jest.mock('../../containers/sourcerer'); const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; jest.mock('use-resize-observer/polyfilled'); @@ -45,9 +44,10 @@ const to = '2019-08-27T22:10:56.794Z'; const defaultMocks = { browserFields: mockBrowserFields, - indexPatterns: mockIndexPattern, docValueFields: mockDocValueFields, - isLoading: false, + indexPattern: mockIndexPattern, + loading: false, + selectedPatterns: mockIndexNames, }; const utilityBar = (refetch: inputsModel.Refetch, totalCount: number) => ( @@ -63,6 +63,7 @@ const eventsViewerDefaultProps = { end: to, filters: [], id: TimelineId.detectionsPage, + indexNames: mockIndexNames, indexPattern: mockIndexPattern, isLive: false, isLoadingIndexPattern: false, @@ -79,6 +80,7 @@ const eventsViewerDefaultProps = { columnId: 'foo', sortDirection: 'none' as SortDirection, }, + scopeId: SourcererScopeName.timeline, toggleColumn: jest.fn(), utilityBar, }; @@ -86,154 +88,57 @@ const eventsViewerDefaultProps = { describe('EventsViewer', () => { const mount = useMountAppended(); + let testProps = { + defaultModel: eventsDefaultModel, + end: to, + id: 'test-stateful-events-viewer', + start: from, + scopeId: SourcererScopeName.timeline, + }; + beforeEach(() => { (useTimelineEvents as jest.Mock).mockReturnValue([false, mockEventViewerResponse]); - mockUseFetchIndexPatterns.mockImplementation(() => [{ ...defaultMocks }]); }); - - test('it renders the "Showing..." subtitle with the expected event count', async () => { - const wrapper = mount( - - - - ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().text()).toEqual( - 'Showing: 12 events' - ); - }); + beforeAll(() => { + mockUseSourcererScope.mockImplementation(() => defaultMocks); }); - - test('it does NOT render fetch index pattern is loading', async () => { - mockUseFetchIndexPatterns.mockImplementation(() => [{ ...defaultMocks, isLoading: true }]); - - const wrapper = mount( - - - - ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( - false + describe('rendering', () => { + test('it renders the "Showing..." subtitle with the expected event count', () => { + const wrapper = mount( + + + ); - }); - }); - - test('it does NOT render when start is empty', async () => { - mockUseFetchIndexPatterns.mockImplementation(() => [{ ...defaultMocks, isLoading: true }]); - - const wrapper = mount( - - - - ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( - false + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().text()).toEqual( + 'Showing: 12 events' ); }); - }); - test('it does NOT render when end is empty', async () => { - mockUseFetchIndexPatterns.mockImplementation(() => [{ ...defaultMocks, isLoading: true }]); - - const wrapper = mount( - - - - ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( - false + test('it renders the Fields Browser as a settings gear', () => { + const wrapper = mount( + + + ); - }); - }); - - test('it renders the Fields Browser as a settings gear', async () => { - const wrapper = mount( - - - - ); - - await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="show-field-browser"]`).first().exists()).toBe(true); }); - }); - - test('it renders the footer containing the Load More button', async () => { - const wrapper = mount( - - - - ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="timeline-pagination"]`).first().exists()).toBe(true); - }); - }); - - defaultHeaders.forEach((header) => { - test(`it renders the ${header.id} default EventsViewer column header`, async () => { + // TO DO sourcerer @X + test('it renders the footer containing the pagination', () => { const wrapper = mount( - + ); + expect(wrapper.find(`[data-test-subj="timeline-pagination"]`).first().exists()).toBe(true); + }); - await waitFor(() => { - wrapper.update(); + defaultHeaders.forEach((header) => { + test(`it renders the ${header.id} default EventsViewer column header`, () => { + const wrapper = mount( + + + + ); defaultHeaders.forEach((h) => expect(wrapper.find(`[data-test-subj="header-text-${header.id}"]`).first().exists()).toBe( @@ -242,10 +147,58 @@ describe('EventsViewer', () => { ); }); }); + describe('loading', () => { + beforeAll(() => { + mockUseSourcererScope.mockImplementation(() => ({ ...defaultMocks, loading: true })); + }); + test('it does NOT render fetch index pattern is loading', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( + false + ); + }); + + test('it does NOT render when start is empty', () => { + testProps = { + ...testProps, + start: '', + }; + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( + false + ); + }); + + test('it does NOT render when end is empty', () => { + testProps = { + ...testProps, + end: '', + }; + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( + false + ); + }); + }); }); describe('headerFilterGroup', () => { - test('it renders the provided headerFilterGroup', async () => { + test('it renders the provided headerFilterGroup', () => { const wrapper = mount( { /> ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="alerts-table-filter-group"]`).exists()).toBe(true); - }); + expect(wrapper.find(`[data-test-subj="alerts-table-filter-group"]`).exists()).toBe(true); }); - test('it has a visible HeaderFilterGroupWrapper when Resolver is NOT showing, because graphEventId is undefined', async () => { + test('it has a visible HeaderFilterGroupWrapper when Resolver is NOT showing, because graphEventId is undefined', () => { const wrapper = mount( { /> ); - - await waitFor(() => { - wrapper.update(); - - expect( - wrapper.find(`[data-test-subj="header-filter-group-wrapper"]`).first() - ).not.toHaveStyleRule('visibility', 'hidden'); - }); + expect( + wrapper.find(`[data-test-subj="header-filter-group-wrapper"]`).first() + ).not.toHaveStyleRule('visibility', 'hidden'); }); - test('it has a visible HeaderFilterGroupWrapper when Resolver is NOT showing, because graphEventId is an empty string', async () => { + test('it has a visible HeaderFilterGroupWrapper when Resolver is NOT showing, because graphEventId is an empty string', () => { const wrapper = mount( { /> ); - - await waitFor(() => { - wrapper.update(); - - expect( - wrapper.find(`[data-test-subj="header-filter-group-wrapper"]`).first() - ).not.toHaveStyleRule('visibility', 'hidden'); - }); + expect( + wrapper.find(`[data-test-subj="header-filter-group-wrapper"]`).first() + ).not.toHaveStyleRule('visibility', 'hidden'); }); - test('it does NOT have a visible HeaderFilterGroupWrapper when Resolver is showing, because graphEventId is a valid id', async () => { + test('it does NOT have a visible HeaderFilterGroupWrapper when Resolver is showing, because graphEventId is a valid id', () => { const wrapper = mount( { /> ); - - await waitFor(() => { - wrapper.update(); - - expect( - wrapper.find(`[data-test-subj="header-filter-group-wrapper"]`).first() - ).toHaveStyleRule('visibility', 'hidden'); - }); + expect( + wrapper.find(`[data-test-subj="header-filter-group-wrapper"]`).first() + ).toHaveStyleRule('visibility', 'hidden'); }); - test('it (still) renders an invisible headerFilterGroup (to maintain state while hidden) when Resolver is showing, because graphEventId is a valid id', async () => { + test('it (still) renders an invisible headerFilterGroup (to maintain state while hidden) when Resolver is showing, because graphEventId is a valid id', () => { const wrapper = mount( { /> ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="alerts-table-filter-group"]`).exists()).toBe(true); - }); + expect(wrapper.find(`[data-test-subj="alerts-table-filter-group"]`).exists()).toBe(true); }); }); describe('utilityBar', () => { - test('it renders the provided utilityBar when Resolver is NOT showing, because graphEventId is undefined', async () => { + test('it renders the provided utilityBar when Resolver is NOT showing, because graphEventId is undefined', () => { const wrapper = mount( ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="mock-utility-bar"]`).exists()).toBe(true); - }); + expect(wrapper.find(`[data-test-subj="mock-utility-bar"]`).exists()).toBe(true); }); - test('it renders the provided utilityBar when Resolver is NOT showing, because graphEventId is an empty string', async () => { + test('it renders the provided utilityBar when Resolver is NOT showing, because graphEventId is an empty string', () => { const wrapper = mount( ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="mock-utility-bar"]`).exists()).toBe(true); - }); + expect(wrapper.find(`[data-test-subj="mock-utility-bar"]`).exists()).toBe(true); }); - test('it does NOT render the provided utilityBar when Resolver is showing, because graphEventId is a valid id', async () => { + test('it does NOT render the provided utilityBar when Resolver is showing, because graphEventId is a valid id', () => { const wrapper = mount( ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="mock-utility-bar"]`).exists()).toBe(false); - }); + expect(wrapper.find(`[data-test-subj="mock-utility-bar"]`).exists()).toBe(false); }); }); describe('header inspect button', () => { - test('it renders the inspect button when Resolver is NOT showing, because graphEventId is undefined', async () => { + test('it renders the inspect button when Resolver is NOT showing, because graphEventId is undefined', () => { const wrapper = mount( ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBe(true); - }); + expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBe(true); }); - test('it renders the inspect button when Resolver is NOT showing, because graphEventId is an empty string', async () => { + test('it renders the inspect button when Resolver is NOT showing, because graphEventId is an empty string', () => { const wrapper = mount( ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBe(true); - }); + expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBe(true); }); - test('it does NOT render the inspect button when Resolver is showing, because graphEventId is a valid id', async () => { + test('it does NOT render the inspect button when Resolver is showing, because graphEventId is a valid id', () => { const wrapper = mount( ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBe(false); - }); + expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBe(false); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 2998bd031d674e..2c8c8136a47332 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -95,6 +95,7 @@ interface Props { headerFilterGroup?: React.ReactNode; height?: number; id: string; + indexNames: string[]; indexPattern: IIndexPattern; isLive: boolean; isLoadingIndexPattern: boolean; @@ -121,6 +122,7 @@ const EventsViewerComponent: React.FC = ({ filters, headerFilterGroup, id, + indexNames, indexPattern, isLive, isLoadingIndexPattern, @@ -213,7 +215,7 @@ const EventsViewerComponent: React.FC = ({ fields, filterQuery: combinedQueries!.filterQuery, id, - indexPattern, + indexNames, limit: itemsPerPage, sort: sortField, startDate: start, diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx index 8c61281422c2a6..9a3c0fa1cad2e2 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx @@ -10,14 +10,13 @@ import useResizeObserver from 'use-resize-observer/polyfilled'; import '../../mock/match_media'; // we don't have the types for waitFor just yet, so using "as waitFor" until when we do import { wait as waitFor } from '@testing-library/react'; -import { mockIndexPattern, TestProviders } from '../../mock'; +import { TestProviders } from '../../mock'; import { useMountAppended } from '../../utils/use_mount_appended'; import { mockEventViewerResponse } from './mock'; import { StatefulEventsViewer } from '.'; -import { useFetchIndexPatterns } from '../../../detections/containers/detection_engine/rules/fetch_index_patterns'; -import { mockBrowserFields } from '../../containers/source/mock'; import { eventsDefaultModel } from './default_model'; +import { SourcererScopeName } from '../../store/sourcerer/model'; import { useTimelineEvents } from '../../../timelines/containers'; jest.mock('../../../timelines/containers', () => ({ @@ -26,15 +25,6 @@ jest.mock('../../../timelines/containers', () => ({ jest.mock('../../components/url_state/normalize_time_range.ts'); -const mockUseFetchIndexPatterns: jest.Mock = useFetchIndexPatterns as jest.Mock; -jest.mock('../../../detections/containers/detection_engine/rules/fetch_index_patterns'); -mockUseFetchIndexPatterns.mockImplementation(() => [ - { - browserFields: mockBrowserFields, - indexPatterns: mockIndexPattern, - }, -]); - const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; jest.mock('use-resize-observer/polyfilled'); mockUseResizeObserver.mockImplementation(() => ({})); @@ -42,6 +32,14 @@ mockUseResizeObserver.mockImplementation(() => ({})); const from = '2019-08-27T22:10:56.794Z'; const to = '2019-08-26T22:10:56.791Z'; +const testProps = { + defaultModel: eventsDefaultModel, + end: to, + indexNames: [], + id: 'test-stateful-events-viewer', + scopeId: SourcererScopeName.default, + start: from, +}; describe('StatefulEventsViewer', () => { const mount = useMountAppended(); @@ -50,12 +48,7 @@ describe('StatefulEventsViewer', () => { test('it renders the events viewer', async () => { const wrapper = mount( - + ); @@ -70,12 +63,7 @@ describe('StatefulEventsViewer', () => { test('it renders InspectButtonContainer', async () => { const wrapper = mount( - + ); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index e4520dab4626a8..cd43c7e4930657 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -9,7 +9,6 @@ import { connect, ConnectedProps } from 'react-redux'; import deepEqual from 'fast-deep-equal'; import styled from 'styled-components'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel, inputsSelectors, State } from '../../store'; import { inputsActions } from '../../store/actions'; import { timelineSelectors, timelineActions } from '../../../timelines/store/timeline'; @@ -20,11 +19,11 @@ import { } from '../../../timelines/store/timeline/model'; import { OnChangeItemsPerPage } from '../../../timelines/components/timeline/events'; import { Filter } from '../../../../../../../src/plugins/data/public'; -import { useUiSetting } from '../../lib/kibana'; import { EventsViewer } from './events_viewer'; -import { useFetchIndexPatterns } from '../../../detections/containers/detection_engine/rules/fetch_index_patterns'; import { InspectButtonContainer } from '../inspect'; import { useFullScreen } from '../../containers/use_full_screen'; +import { SourcererScopeName } from '../../store/sourcerer/model'; +import { useSourcererScope } from '../../containers/sourcerer'; const DEFAULT_EVENTS_VIEWER_HEIGHT = 652; @@ -35,10 +34,10 @@ const FullScreenContainer = styled.div<{ $isFullScreen: boolean }>` `; export interface OwnProps { - defaultIndices?: string[]; defaultModel: SubsetTimelineModel; end: string; id: string; + scopeId: SourcererScopeName; start: string; headerFilterGroup?: React.ReactNode; pageFilters?: Filter[]; @@ -52,7 +51,6 @@ const StatefulEventsViewerComponent: React.FC = ({ columns, dataProviders, deletedEventIds, - defaultIndices, deleteEventQuery, end, excludedRowRendererIds, @@ -67,6 +65,7 @@ const StatefulEventsViewerComponent: React.FC = ({ query, removeColumn, start, + scopeId, showCheckboxes, sort, updateItemsPerPage, @@ -75,13 +74,13 @@ const StatefulEventsViewerComponent: React.FC = ({ // If truthy, the graph viewer (Resolver) is showing graphEventId, }) => { - const [ - { docValueFields, browserFields, indexPatterns, isLoading: isLoadingIndexPattern }, - ] = useFetchIndexPatterns( - defaultIndices ?? useUiSetting(DEFAULT_INDEX_KEY), - 'events_viewer' - ); - + const { + browserFields, + docValueFields, + indexPattern, + selectedPatterns, + loading: isLoadingIndexPattern, + } = useSourcererScope(scopeId); const { globalFullScreen } = useFullScreen(); useEffect(() => { @@ -90,6 +89,7 @@ const StatefulEventsViewerComponent: React.FC = ({ id, columns, excludedRowRendererIds, + indexNames: selectedPatterns, sort, itemsPerPage, showCheckboxes, @@ -144,7 +144,8 @@ const StatefulEventsViewerComponent: React.FC = ({ isLoadingIndexPattern={isLoadingIndexPattern} filters={globalFilters} headerFilterGroup={headerFilterGroup} - indexPattern={indexPatterns} + indexNames={selectedPatterns} + indexPattern={indexPattern} isLive={isLive} itemsPerPage={itemsPerPage!} itemsPerPageOptions={itemsPerPageOptions!} @@ -222,8 +223,8 @@ export const StatefulEventsViewer = connector( StatefulEventsViewerComponent, (prevProps, nextProps) => prevProps.id === nextProps.id && + prevProps.scopeId === nextProps.scopeId && deepEqual(prevProps.columns, nextProps.columns) && - deepEqual(prevProps.defaultIndices, nextProps.defaultIndices) && deepEqual(prevProps.dataProviders, nextProps.dataProviders) && deepEqual(prevProps.excludedRowRendererIds, nextProps.excludedRowRendererIds) && prevProps.deletedEventIds === nextProps.deletedEventIds && diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx index 691a7d99d9345f..ed1c1c1cdad1f9 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx @@ -13,7 +13,7 @@ import { act } from 'react-dom/test-utils'; import { AddExceptionModal } from './'; import { useCurrentUser } from '../../../../common/lib/kibana'; import { getExceptionListSchemaMock } from '../../../../../../lists/common/schemas/response/exception_list_schema.mock'; -import { useFetchIndexPatterns } from '../../../../detections/containers/detection_engine/rules'; +import { useFetchIndex } from '../../../containers/source'; import { stubIndexPattern } from 'src/plugins/data/common/index_patterns/index_pattern.stub'; import { useAddOrUpdateException } from '../use_add_exception'; import { useFetchOrCreateRuleExceptionList } from '../use_fetch_or_create_rule_exception_list'; @@ -28,6 +28,7 @@ import { ExceptionListItemSchema } from '../../../../../../lists/common'; jest.mock('../../../../detections/containers/detection_engine/alerts/use_signal_index'); jest.mock('../../../../common/lib/kibana'); +jest.mock('../../../containers/source'); jest.mock('../../../../detections/containers/detection_engine/rules'); jest.mock('../use_add_exception'); jest.mock('../use_fetch_or_create_rule_exception_list'); @@ -59,9 +60,9 @@ describe('When the add exception modal is opened', () => { loading: false, signalIndexName: 'mock-siem-signals-index', })); - (useFetchIndexPatterns as jest.Mock).mockImplementation(() => [ + (useFetchIndex as jest.Mock).mockImplementation(() => [ + false, { - isLoading: false, indexPatterns: stubIndexPattern, }, ]); @@ -77,9 +78,9 @@ describe('When the add exception modal is opened', () => { let wrapper: ReactWrapper; beforeEach(() => { // Mocks one of the hooks as loading - (useFetchIndexPatterns as jest.Mock).mockImplementation(() => [ + (useFetchIndex as jest.Mock).mockImplementation(() => [ + true, { - isLoading: true, indexPatterns: stubIndexPattern, }, ]); @@ -244,9 +245,9 @@ describe('When the add exception modal is opened', () => { }; beforeEach(() => { // Mocks the index patterns to contain the pre-populated endpoint fields so that the exception qualifies as bulk closable - (useFetchIndexPatterns as jest.Mock).mockImplementation(() => [ + (useFetchIndex as jest.Mock).mockImplementation(() => [ + false, { - isLoading: false, indexPatterns: { ...stubIndexPattern, fields: [ diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx index 721e53732c0936..e945461f53e81c 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx @@ -50,8 +50,8 @@ import { getMappedNonEcsValue, } from '../helpers'; import { ErrorInfo, ErrorCallout } from '../error_callout'; -import { useFetchIndexPatterns } from '../../../../detections/containers/detection_engine/rules'; import { ExceptionsBuilderExceptionItem } from '../types'; +import { useFetchIndex } from '../../../containers/source'; export interface AddExceptionModalBaseProps { ruleName: string; @@ -122,14 +122,13 @@ export const AddExceptionModal = memo(function AddExceptionModal({ const [fetchOrCreateListError, setFetchOrCreateListError] = useState(null); const { addError, addSuccess, addWarning } = useAppToasts(); const { loading: isSignalIndexLoading, signalIndexName } = useSignalIndex(); - const [ - { isLoading: isSignalIndexPatternLoading, indexPatterns: signalIndexPatterns }, - ] = useFetchIndexPatterns(signalIndexName !== null ? [signalIndexName] : [], 'signals'); - - const [{ isLoading: isIndexPatternLoading, indexPatterns }] = useFetchIndexPatterns( - ruleIndices, - 'rules' + const memoSignalIndexName = useMemo(() => (signalIndexName !== null ? [signalIndexName] : []), [ + signalIndexName, + ]); + const [isSignalIndexPatternLoading, { indexPatterns: signalIndexPatterns }] = useFetchIndex( + memoSignalIndexName ); + const [isIndexPatternLoading, { indexPatterns }] = useFetchIndex(ruleIndices); const onError = useCallback( (error: Error): void => { diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.test.tsx index c724e6a2c711fd..d5d2091cc9bc87 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.test.tsx @@ -12,7 +12,7 @@ import { act } from 'react-dom/test-utils'; import { EditExceptionModal } from './'; import { useCurrentUser } from '../../../../common/lib/kibana'; -import { useFetchIndexPatterns } from '../../../../detections/containers/detection_engine/rules'; +import { useFetchIndex } from '../../../containers/source'; import { stubIndexPattern, stubIndexPatternWithFields, @@ -26,6 +26,7 @@ import * as builder from '../builder'; jest.mock('../../../../common/lib/kibana'); jest.mock('../../../../detections/containers/detection_engine/rules'); jest.mock('../use_add_exception'); +jest.mock('../../../containers/source'); jest.mock('../use_fetch_or_create_rule_exception_list'); jest.mock('../../../../detections/containers/detection_engine/alerts/use_signal_index'); jest.mock('../builder'); @@ -50,9 +51,9 @@ describe('When the edit exception modal is opened', () => { { isLoading: false }, jest.fn(), ]); - (useFetchIndexPatterns as jest.Mock).mockImplementation(() => [ + (useFetchIndex as jest.Mock).mockImplementation(() => [ + false, { - isLoading: false, indexPatterns: stubIndexPatternWithFields, }, ]); @@ -67,9 +68,9 @@ describe('When the edit exception modal is opened', () => { describe('when the modal is loading', () => { let wrapper: ReactWrapper; beforeEach(() => { - (useFetchIndexPatterns as jest.Mock).mockImplementation(() => [ + (useFetchIndex as jest.Mock).mockImplementation(() => [ + true, { - isLoading: true, indexPatterns: stubIndexPattern, }, ]); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx index 5dbf319c3299da..128686428598c3 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx @@ -21,7 +21,8 @@ import { EuiText, EuiCallOut, } from '@elastic/eui'; -import { useFetchIndexPatterns } from '../../../../detections/containers/detection_engine/rules'; + +import { useFetchIndex } from '../../../containers/source'; import { useSignalIndex } from '../../../../detections/containers/detection_engine/alerts/use_signal_index'; import { useRuleAsync } from '../../../../detections/containers/detection_engine/rules/use_rule_async'; import { @@ -108,15 +109,12 @@ export const EditExceptionModal = memo(function EditExceptionModal({ >([]); const { addError, addSuccess } = useAppToasts(); const { loading: isSignalIndexLoading, signalIndexName } = useSignalIndex(); - const [ - { isLoading: isSignalIndexPatternLoading, indexPatterns: signalIndexPatterns }, - ] = useFetchIndexPatterns(signalIndexName !== null ? [signalIndexName] : [], 'signals'); - - const [{ isLoading: isIndexPatternLoading, indexPatterns }] = useFetchIndexPatterns( - ruleIndices, - 'rules' + const [isSignalIndexPatternLoading, { indexPatterns: signalIndexPatterns }] = useFetchIndex( + signalIndexName !== null ? [signalIndexName] : [] ); + const [isIndexPatternLoading, { indexPatterns }] = useFetchIndex(ruleIndices); + const handleExceptionUpdateError = useCallback( (error: Error, statusCode: number | null, message: string | null) => { if (error.message.includes('Conflict')) { diff --git a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx index e05e3c2e9aeb13..5b4dd2e9728bba 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx @@ -18,7 +18,6 @@ import { getAppOverviewUrl } from '../link_to'; import { MlPopover } from '../ml_popover/ml_popover'; import { SiemNavigation } from '../navigation'; import * as i18n from './translations'; -import { useWithSource } from '../../containers/source'; import { useGetUrlSearch } from '../navigation/use_get_url_search'; import { useKibana } from '../../lib/kibana'; import { APP_ID, ADD_DATA_PATH, APP_DETECTIONS_PATH } from '../../../../common/constants'; @@ -58,11 +57,12 @@ interface HeaderGlobalProps { hideDetectionEngine?: boolean; } export const HeaderGlobal = React.memo(({ hideDetectionEngine = false }) => { - const { indicesExist } = useWithSource(); const { globalHeaderPortalNode } = useGlobalHeaderPortal(); const { globalFullScreen } = useFullScreen(); const search = useGetUrlSearch(navTabs.overview); - const { navigateToApp } = useKibana().services.application; + const { application, http } = useKibana().services; + const { navigateToApp } = application; + const basePath = http.basePath.get(); const goToOverview = useCallback( (ev) => { ev.preventDefault(); @@ -104,7 +104,7 @@ export const HeaderGlobal = React.memo(({ hideDetectionEngine - {indicesExist && window.location.pathname.includes(APP_DETECTIONS_PATH) && ( + {window.location.pathname.includes(APP_DETECTIONS_PATH) && ( @@ -113,7 +113,7 @@ export const HeaderGlobal = React.memo(({ hideDetectionEngine {i18n.BUTTON_ADD_DATA} diff --git a/x-pack/plugins/security_solution/public/common/components/header_page/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/header_page/__snapshots__/index.test.tsx.snap index a100f5e4f93b48..a2a36b3fe1d3b2 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_page/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/header_page/__snapshots__/index.test.tsx.snap @@ -36,5 +36,8 @@ exports[`HeaderPage it renders 1`] = `

    +

`; diff --git a/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx index 62880e7510cd2b..0cb721bb5382f2 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx @@ -15,6 +15,8 @@ import { Title } from './title'; import { DraggableArguments, BadgeOptions, TitleProp } from './types'; import { useFormatUrl } from '../link_to'; import { SecurityPageName } from '../../../app/types'; +import { Sourcerer } from '../sourcerer'; +import { SourcererScopeName } from '../../store/sourcerer/model'; interface HeaderProps { border?: boolean; @@ -72,6 +74,7 @@ export interface HeaderPageProps extends HeaderProps { badgeOptions?: BadgeOptions; children?: React.ReactNode; draggableArguments?: DraggableArguments; + hideSourcerer?: boolean; subtitle?: SubtitleProps['items']; subtitle2?: SubtitleProps['items']; title: TitleProp; @@ -84,6 +87,7 @@ const HeaderPageComponent: React.FC = ({ border, children, draggableArguments, + hideSourcerer = false, isLoading, subtitle, subtitle2, @@ -138,6 +142,7 @@ const HeaderPageComponent: React.FC = ({ )} + {!hideSourcerer && } ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx index 9473ba67a1c4fa..c2800b0705b43e 100644 --- a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx @@ -37,7 +37,7 @@ describe('Last Event Time Stat', () => { ]); const wrapper = mount( - + ); expect(wrapper.html()).toBe( @@ -54,7 +54,7 @@ describe('Last Event Time Stat', () => { ]); const wrapper = mount( - + ); expect(wrapper.html()).toBe('Last event: 12 minutes ago'); @@ -69,7 +69,7 @@ describe('Last Event Time Stat', () => { ]); const wrapper = mount( - + ); @@ -85,7 +85,7 @@ describe('Last Event Time Stat', () => { ]); const wrapper = mount( - + ); expect(wrapper.html()).toContain(getEmptyValue()); diff --git a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.tsx b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.tsx index e9e8e7a03017cb..d508040f84239b 100644 --- a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.tsx @@ -8,58 +8,65 @@ import { EuiIcon, EuiLoadingSpinner, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { memo } from 'react'; +import { DocValueFields } from '../../../../common/search_strategy'; import { LastEventIndexKey } from '../../../graphql/types'; import { useTimelineLastEventTime } from '../../containers/events/last_event_time'; import { getEmptyTagValue } from '../empty_value'; import { FormattedRelativePreferenceDate } from '../formatted_date'; export interface LastEventTimeProps { + docValueFields: DocValueFields[]; hostName?: string; indexKey: LastEventIndexKey; ip?: string; + indexNames: string[]; } -export const LastEventTime = memo(({ hostName, indexKey, ip }) => { - const [loading, { lastSeen, errorMessage }] = useTimelineLastEventTime({ - indexKey, - details: { - hostName, - ip, - }, - }); +export const LastEventTime = memo( + ({ docValueFields, hostName, indexKey, ip, indexNames }) => { + const [loading, { lastSeen, errorMessage }] = useTimelineLastEventTime({ + docValueFields, + indexKey, + indexNames, + details: { + hostName, + ip, + }, + }); + + if (errorMessage != null) { + return ( + + + + ); + } - if (errorMessage != null) { return ( - - - + <> + {loading && } + {!loading && lastSeen != null && new Date(lastSeen).toString() === 'Invalid Date' + ? lastSeen + : !loading && + lastSeen != null && ( + , + }} + /> + )} + {!loading && lastSeen == null && getEmptyTagValue()} + ); } - - return ( - <> - {loading && } - {!loading && lastSeen != null && new Date(lastSeen).toString() === 'Invalid Date' - ? lastSeen - : !loading && - lastSeen != null && ( - , - }} - /> - )} - {!loading && lastSeen == null && getEmptyTagValue()} - - ); -}); +); LastEventTime.displayName = 'LastEventTime'; diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx index 7286c6b7436929..99dc8a802b33df 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx @@ -47,6 +47,7 @@ describe('Matrix Histogram Component', () => { errorMessage: 'error', histogramType: MatrixHistogramType.alerts, id: 'mockId', + indexNames: [], isInspected: false, isPtrIncluded: false, setQuery: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx index 485ca4c93133ac..e7d7e60a3c4088 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx @@ -37,7 +37,6 @@ export type MatrixHistogramComponentProps = MatrixHistogramProps & hideHistogramIfEmpty?: boolean; histogramType: MatrixHistogramType; id: string; - indexToAdd?: string[] | null; legendPosition?: Position; mapping?: MatrixHistogramMappingTypes; showSpacer?: boolean; @@ -72,7 +71,7 @@ export const MatrixHistogramComponent: React.FC = histogramType, hideHistogramIfEmpty = false, id, - indexToAdd, + indexNames, legendPosition, mapping, panelHeight = DEFAULT_PANEL_HEIGHT, @@ -136,7 +135,7 @@ export const MatrixHistogramComponent: React.FC = errorMessage, filterQuery, histogramType, - indexToAdd, + indexNames, startDate, stackByField: selectedStackByOption.value, }); diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts index fc1df4d8ca85f5..9a892110bde43d 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts @@ -59,6 +59,7 @@ interface MatrixHistogramBasicProps { export interface MatrixHistogramQueryProps { endDate: string; errorMessage: string; + indexNames: string[]; filterQuery?: ESQuery | string | undefined; setAbsoluteRangeDatePicker?: ActionCreator<{ id: InputsModelId; @@ -68,7 +69,6 @@ export interface MatrixHistogramQueryProps { setAbsoluteRangeDatePickerTarget?: InputsModelId; stackByField: string; startDate: string; - indexToAdd?: string[] | null; histogramType: MatrixHistogramType; } diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts index 89aa77106933e5..da5099f61e9b2d 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts @@ -105,6 +105,7 @@ const getMockObject = ( }, }, }, + sourcerer: {}, }); const getUrlForAppMock = (appId: string, options?: { path?: string; absolute?: boolean }) => @@ -130,7 +131,7 @@ describe('Navigation Breadcrumbs', () => { }, { href: - "securitySolution:hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:hosts?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", text: 'Hosts', }, { @@ -150,7 +151,7 @@ describe('Navigation Breadcrumbs', () => { { text: 'Network', href: - "securitySolution:network?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:network?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: 'Flows', @@ -169,7 +170,7 @@ describe('Navigation Breadcrumbs', () => { { text: 'Timelines', href: - "securitySolution:timelines?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:timelines?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, ]); }); @@ -184,12 +185,12 @@ describe('Navigation Breadcrumbs', () => { { text: 'Hosts', href: - "securitySolution:hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:hosts?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: 'siem-kibana', href: - "securitySolution:hosts/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:hosts/siem-kibana?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: 'Authentications', href: '' }, ]); @@ -205,11 +206,11 @@ describe('Navigation Breadcrumbs', () => { { text: 'Network', href: - "securitySolution:network?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:network?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: ipv4, - href: `securitySolution:network/ip/${ipv4}/source?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, + href: `securitySolution:network/ip/${ipv4}/source?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, }, { text: 'Flows', href: '' }, ]); @@ -225,11 +226,11 @@ describe('Navigation Breadcrumbs', () => { { text: 'Network', href: - "securitySolution:network?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:network?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: ipv6, - href: `securitySolution:network/ip/${ipv6Encoded}/source?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, + href: `securitySolution:network/ip/${ipv6Encoded}/source?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, }, { text: 'Flows', href: '' }, ]); @@ -245,7 +246,7 @@ describe('Navigation Breadcrumbs', () => { { text: 'Detections', href: - "securitySolution:detections?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:detections?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, ]); }); @@ -259,7 +260,7 @@ describe('Navigation Breadcrumbs', () => { { text: 'Cases', href: - "securitySolution:case?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:case?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, ]); }); @@ -280,11 +281,11 @@ describe('Navigation Breadcrumbs', () => { { text: 'Cases', href: - "securitySolution:case?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:case?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: sampleCase.name, - href: `securitySolution:case/${sampleCase.id}?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, + href: `securitySolution:case/${sampleCase.id}?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, }, ]); }); @@ -311,12 +312,12 @@ describe('Navigation Breadcrumbs', () => { { text: 'Hosts', href: - "securitySolution:hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:hosts?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: 'siem-kibana', href: - "securitySolution:hosts/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:hosts/siem-kibana?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: 'Authentications', href: '' }, ]); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts b/x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts index 8f5a3ac63fa1ad..ed71f55fd0161c 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts @@ -19,12 +19,19 @@ import { import { Query, Filter } from '../../../../../../../src/plugins/data/public'; import { SearchNavTab } from './types'; +import { SourcererScopePatterns } from '../../store/sourcerer/model'; export const getSearch = (tab: SearchNavTab, urlState: UrlState): string => { if (tab && tab.urlKey != null && URL_STATE_KEYS[tab.urlKey] != null) { return URL_STATE_KEYS[tab.urlKey].reduce( (myLocation: Location, urlKey: KeyUrlState) => { - let urlStateToReplace: UrlInputsModel | Query | Filter[] | TimelineUrl | string = ''; + let urlStateToReplace: + | Filter[] + | Query + | SourcererScopePatterns + | TimelineUrl + | UrlInputsModel + | string = ''; if (urlKey === CONSTANTS.appQuery && urlState.query != null) { if (urlState.query.query === '') { @@ -40,6 +47,8 @@ export const getSearch = (tab: SearchNavTab, urlState: UrlState): string => { } } else if (urlKey === CONSTANTS.timerange) { urlStateToReplace = urlState[CONSTANTS.timerange]; + } else if (urlKey === CONSTANTS.sourcerer) { + urlStateToReplace = urlState[CONSTANTS.sourcerer]; } else if (urlKey === CONSTANTS.timeline && urlState[CONSTANTS.timeline] != null) { const timeline = urlState[CONSTANTS.timeline]; if (timeline.id === '') { diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx index 16cb19f5a0c146..102ed7851e57d2 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx @@ -78,6 +78,7 @@ describe('SIEM Navigation', () => { }, [CONSTANTS.appQuery]: { query: '', language: 'kuery' }, [CONSTANTS.filters]: [], + [CONSTANTS.sourcerer]: {}, [CONSTANTS.timeline]: { id: '', isOpen: false, @@ -145,6 +146,7 @@ describe('SIEM Navigation', () => { pageName: 'hosts', pathName: '/', search: '', + sourcerer: {}, state: undefined, tabName: 'authentications', query: { query: '', language: 'kuery' }, @@ -252,6 +254,7 @@ describe('SIEM Navigation', () => { query: { language: 'kuery', query: '' }, savedQuery: undefined, search: '', + sourcerer: {}, state: undefined, tabName: 'authentications', timeline: { id: '', isOpen: false, graphEventId: '' }, diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx index 5ee35e7da0f3ee..b149488ff38a75 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx @@ -40,19 +40,20 @@ export const SiemNavigationComponent: React.FC< if (pathName || pageName) { setBreadcrumbs( { - query: urlState.query, detailName, filters: urlState.filters, + flowTarget, navTabs, pageName, pathName, + query: urlState.query, savedQuery: urlState.savedQuery, search, + sourcerer: urlState.sourcerer, + state, tabName, - flowTarget, - timerange: urlState.timerange, timeline: urlState.timeline, - state, + timerange: urlState.timerange, }, chrome, getUrlForApp @@ -69,6 +70,7 @@ export const SiemNavigationComponent: React.FC< navTabs={navTabs} pageName={pageName} pathName={pathName} + sourcerer={urlState.sourcerer} savedQuery={urlState.savedQuery} tabName={tabName} timeline={urlState.timeline} diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx index b25cf3779801b7..5c69edbabdc665 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx @@ -68,6 +68,7 @@ describe('Tab Navigation', () => { }, [CONSTANTS.appQuery]: { query: 'host.name:"siem-es"', language: 'kuery' }, [CONSTANTS.filters]: [], + [CONSTANTS.sourcerer]: {}, [CONSTANTS.timeline]: { id: '', isOpen: false, @@ -126,6 +127,7 @@ describe('Tab Navigation', () => { }, [CONSTANTS.appQuery]: { query: 'host.name:"siem-es"', language: 'kuery' }, [CONSTANTS.filters]: [], + [CONSTANTS.sourcerer]: {}, [CONSTANTS.timeline]: { id: '', isOpen: false, diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx index 217ad0e58570f5..3eb66b5591b85e 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx @@ -94,10 +94,17 @@ export const TabNavigationComponent = (props: TabNavigationProps) => { () => Object.values(navTabs).map((tab) => { const isSelected = selectedTabId === tab.id; - const { query, filters, savedQuery, timerange, timeline } = props; - const search = getSearch(tab, { query, filters, savedQuery, timerange, timeline }); + const { filters, query, savedQuery, sourcerer, timeline, timerange } = props; + const search = getSearch(tab, { + filters, + query, + savedQuery, + sourcerer, + timeline, + timerange, + }); const hrefWithSearch = - tab.href + getSearch(tab, { query, filters, savedQuery, timerange, timeline }); + tab.href + getSearch(tab, { filters, query, savedQuery, sourcerer, timeline, timerange }); return ( { - const original = jest.requireActual('../../containers/sourcerer'); +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); return { ...original, - useManageSource: () => mockManageSource, + useDispatch: () => mockDispatch, }; }); const mockOptions = [ - { label: 'auditbeat-*', key: 'auditbeat-*-0', value: 'auditbeat-*', checked: 'on' }, - { label: 'endgame-*', key: 'endgame-*-1', value: 'endgame-*', checked: 'on' }, - { label: 'filebeat-*', key: 'filebeat-*-2', value: 'filebeat-*', checked: 'on' }, - { label: 'logs-*', key: 'logs-*-3', value: 'logs-*', checked: 'on' }, - { label: 'packetbeat-*', key: 'packetbeat-*-4', value: 'packetbeat-*', checked: undefined }, - { label: 'winlogbeat-*', key: 'winlogbeat-*-5', value: 'winlogbeat-*', checked: 'on' }, - { - label: 'apm-*-transaction*', - key: 'apm-*-transaction*-0', - value: 'apm-*-transaction*', - disabled: true, - checked: undefined, - }, - { - label: 'blobbeat-*', - key: 'blobbeat-*-1', - value: 'blobbeat-*', - disabled: true, - checked: undefined, - }, + { label: 'apm-*-transaction*', value: 'apm-*-transaction*' }, + { label: 'auditbeat-*', value: 'auditbeat-*' }, + { label: 'endgame-*', value: 'endgame-*' }, + { label: 'filebeat-*', value: 'filebeat-*' }, + { label: 'logs-*', value: 'logs-*' }, + { label: 'packetbeat-*', value: 'packetbeat-*' }, + { label: 'winlogbeat-*', value: 'winlogbeat-*' }, ]; +const defaultProps = { + scope: sourcererModel.SourcererScopeName.default, +}; describe('Sourcerer component', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + const state: State = mockGlobalState; + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + kibanaObservable, + storage + ); + + beforeEach(() => { + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + kibanaObservable, + storage + ); + }); + // Using props callback instead of simulating clicks, // because EuiSelectable uses a virtualized list, which isn't easily testable via test subjects - it('Mounts with correct options selected and disabled', () => { - const wrapper = mount(); + it('Mounts with all options selected', () => { + const wrapper = mount( + + + + ); wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); - expect( - wrapper.find(`[data-test-subj="indexPattern-switcher"]`).first().prop('options') + wrapper.find(`[data-test-subj="indexPattern-switcher"]`).first().prop('selectedOptions') ).toEqual(mockOptions); }); - it('onChange calls updateSourceGroupIndicies', () => { - const wrapper = mount(); - wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); - - const switcherOnChange = wrapper - .find(`[data-test-subj="indexPattern-switcher"]`) - .first() - .prop('onChange'); - // @ts-ignore - switcherOnChange([mockOptions[0], mockOptions[1]]); - expect(updateSourceGroupIndicies).toHaveBeenCalledWith(SecurityPageName.default, [ - mockOptions[0].value, - mockOptions[1].value, - ]); - }); - it('Disabled options have icon tooltip', () => { - const wrapper = mount(); - wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); - // @ts-ignore - const Rendered = wrapper - .find(`[data-test-subj="indexPattern-switcher"]`) - .first() - .prop('renderOption')( - { - label: 'blobbeat-*', - key: 'blobbeat-*-1', - value: 'blobbeat-*', - disabled: true, - checked: undefined, + it('Mounts with some options selected', () => { + const state2 = { + ...mockGlobalState, + sourcerer: { + ...mockGlobalState.sourcerer, + sourcererScopes: { + ...mockGlobalState.sourcerer.sourcererScopes, + [SourcererScopeName.default]: { + ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.default], + loading: false, + selectedPatterns: [DEFAULT_INDEX_PATTERN[0]], + }, + }, }, - '' + }; + + store = createStore( + state2, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + kibanaObservable, + storage + ); + const wrapper = mount( + + + ); - expect(Rendered.props.children[1].props.content).toEqual(i18n.DISABLED_INDEX_PATTERNS); + wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); + expect( + wrapper.find(`[data-test-subj="indexPattern-switcher"]`).first().prop('selectedOptions') + ).toEqual([mockOptions[0]]); }); - - it('Button links to index path', () => { - const wrapper = mount(); + it('onChange calls updateSourcererScopeIndices', async () => { + const wrapper = mount( + + + + ); + expect(true).toBeTruthy(); wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); - expect(wrapper.find(`[data-test-subj="add-index"]`).first().prop('href')).toEqual( - ADD_INDEX_PATH + await act(async () => { + ((wrapper.find(EuiComboBox).props() as unknown) as { + onChange: (a: EuiComboBoxOptionOption[]) => void; + }).onChange([mockOptions[0], mockOptions[1]]); + await waitFor(() => { + wrapper.update(); + }); + }); + wrapper.find(`[data-test-subj="add-index"]`).first().simulate('click'); + + expect(mockDispatch).toHaveBeenCalledWith( + sourcererActions.setSelectedIndexPatterns({ + id: SourcererScopeName.default, + selectedPatterns: [mockOptions[0].value, mockOptions[1].value], + }) ); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx index 6275ce19c36087..7a74f5bf2247f5 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx @@ -4,50 +4,122 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback, useMemo, useState } from 'react'; import { EuiButton, EuiButtonEmpty, - EuiHighlight, - EuiIconTip, + EuiComboBox, + EuiComboBoxOptionOption, + EuiIcon, + EuiFlexGroup, + EuiFlexItem, EuiPopover, - EuiPopoverFooter, EuiPopoverTitle, - EuiSelectable, + EuiSpacer, + EuiText, + EuiToolTip, } from '@elastic/eui'; -import { EuiSelectableOption } from '@elastic/eui/src/components/selectable/selectable_option'; -import { useManageSource } from '../../containers/sourcerer'; +import deepEqual from 'fast-deep-equal'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import styled from 'styled-components'; + import * as i18n from './translations'; import { SOURCERER_FEATURE_FLAG_ON } from '../../containers/sourcerer/constants'; -import { ADD_INDEX_PATH } from '../../../../common/constants'; - -export const MaybeSourcerer = React.memo(() => { - const { - activeSourceGroupId, - availableIndexPatterns, - getManageSourceGroupById, - isIndexPatternsLoading, - updateSourceGroupIndicies, - } = useManageSource(); - const { defaultPatterns, indexPatterns: selectedOptions, loading: loadingIndices } = useMemo( - () => getManageSourceGroupById(activeSourceGroupId), - [getManageSourceGroupById, activeSourceGroupId] +import { sourcererActions, sourcererModel } from '../../store/sourcerer'; +import { State } from '../../store'; +import { getSourcererScopeSelector, SourcererScopeSelector } from './selectors'; + +const PopoverContent = styled.div` + width: 600px; +`; + +const ResetButton = styled(EuiButtonEmpty)` + width: fit-content; +`; +interface SourcererComponentProps { + scope: sourcererModel.SourcererScopeName; +} + +export const SourcererComponent = React.memo(({ scope: scopeId }) => { + const dispatch = useDispatch(); + const sourcererScopeSelector = useMemo(getSourcererScopeSelector, []); + const { configIndexPatterns, kibanaIndexPatterns, sourcererScope } = useSelector< + State, + SourcererScopeSelector + >((state) => sourcererScopeSelector(state, scopeId), deepEqual); + const { selectedPatterns, loading } = sourcererScope; + const [isPopoverOpen, setPopoverIsOpen] = useState(false); + const [selectedOptions, setSelectedOptions] = useState>>( + selectedPatterns.map((indexSelected) => ({ + label: indexSelected, + value: indexSelected, + })) ); - const loading = useMemo(() => loadingIndices || isIndexPatternsLoading, [ - isIndexPatternsLoading, - loadingIndices, - ]); + const setPopoverIsOpenCb = useCallback(() => setPopoverIsOpen((prevState) => !prevState), []); const onChangeIndexPattern = useCallback( - (newIndexPatterns: string[]) => { - updateSourceGroupIndicies(activeSourceGroupId, newIndexPatterns); + (newSelectedPatterns: string[]) => { + dispatch( + sourcererActions.setSelectedIndexPatterns({ + id: scopeId, + selectedPatterns: newSelectedPatterns, + }) + ); }, - [activeSourceGroupId, updateSourceGroupIndicies] + [dispatch, scopeId] + ); + + const renderOption = useCallback( + (option) => { + const { value } = option; + if (kibanaIndexPatterns.some((kip) => kip.title === value)) { + return ( + <> + {value} + + ); + } + return <>{value}; + }, + [kibanaIndexPatterns] + ); + + const onChangeCombo = useCallback((newSelectedOptions) => { + setSelectedOptions(newSelectedOptions); + }, []); + + const resetDataSources = useCallback(() => { + setSelectedOptions( + configIndexPatterns.map((indexSelected) => ({ + label: indexSelected, + value: indexSelected, + })) + ); + }, [configIndexPatterns]); + + const handleSaveIndices = useCallback(() => { + onChangeIndexPattern(selectedOptions.map((so) => so.label)); + setPopoverIsOpen(false); + }, [onChangeIndexPattern, selectedOptions]); + + const handleClosePopOver = useCallback(() => { + setPopoverIsOpen(false); + }, []); + + const indexesPatternOptions = useMemo( + () => + [...configIndexPatterns, ...kibanaIndexPatterns.map((kip) => kip.title)].reduce< + Array> + >((acc, index) => { + if (index != null && !acc.some((o) => o.label.includes(index))) { + return [...acc, { label: index, value: index }]; + } + return acc; + }, []), + [configIndexPatterns, kibanaIndexPatterns] ); - const [isPopoverOpen, setPopoverIsOpen] = useState(false); - const setPopoverIsOpenCb = useCallback(() => setPopoverIsOpen((prevState) => !prevState), []); const trigger = useMemo( () => ( { data-test-subj="sourcerer-trigger" flush="left" iconSide="right" - iconType="indexSettings" + iconType="arrowDown" + isLoading={loading} onClick={setPopoverIsOpenCb} size="l" title={i18n.SOURCERER} @@ -63,99 +136,91 @@ export const MaybeSourcerer = React.memo(() => { {i18n.SOURCERER} ), - [setPopoverIsOpenCb] - ); - const options: EuiSelectableOption[] = useMemo( - () => - availableIndexPatterns.map((title, id) => ({ - label: title, - key: `${title}-${id}`, - value: title, - checked: selectedOptions.includes(title) ? 'on' : undefined, - })), - [availableIndexPatterns, selectedOptions] + [setPopoverIsOpenCb, loading] ); - const unSelectableOptions: EuiSelectableOption[] = useMemo( - () => - defaultPatterns - .filter((title) => !availableIndexPatterns.includes(title)) - .map((title, id) => ({ - label: title, - key: `${title}-${id}`, - value: title, - disabled: true, - checked: undefined, - })), - [availableIndexPatterns, defaultPatterns] - ); - const renderOption = useCallback( - (option, searchValue) => ( - <> - {option.label} - {option.disabled ? ( - - ) : null} - + + const comboBox = useMemo( + () => ( + ), - [] + [indexesPatternOptions, onChangeCombo, renderOption, selectedOptions] ); - const onChange = useCallback( - (choices: EuiSelectableOption[]) => { - const choice = choices.reduce( - (acc, { checked, label }) => (checked === 'on' ? [...acc, label] : acc), - [] - ); - onChangeIndexPattern(choice); - }, - [onChangeIndexPattern] + + useEffect(() => { + const newSelecteOptions = selectedPatterns.map((indexSelected) => ({ + label: indexSelected, + value: indexSelected, + })); + setSelectedOptions((prevSelectedOptions) => { + if (!deepEqual(newSelecteOptions, prevSelectedOptions)) { + return newSelecteOptions; + } + return prevSelectedOptions; + }); + }, [selectedPatterns]); + + const tooltipContent = useMemo( + () => (isPopoverOpen ? null : sourcererScope.selectedPatterns.sort().join(', ')), + [isPopoverOpen, sourcererScope.selectedPatterns] ); - const allOptions = useMemo(() => [...options, ...unSelectableOptions], [ - options, - unSelectableOptions, - ]); + return ( - setPopoverIsOpen(false)} - display="block" - panelPaddingSize="s" - ownFocus - > -
- - <> - {i18n.CHANGE_INDEX_PATTERNS} - - - - - {(list, search) => ( - <> - {search} - {list} - - )} - - - - {i18n.ADD_INDEX_PATTERNS} - - -
-
+ + + + + <>{i18n.SELECT_INDEX_PATTERNS} + + + {i18n.INDEX_PATTERNS_SELECTION_LABEL} + + {comboBox} + + + + + {i18n.INDEX_PATTERNS_RESET} + + + + + {i18n.SAVE_INDEX_PATTERNS} + + + + + + ); }); -MaybeSourcerer.displayName = 'Sourcerer'; +SourcererComponent.displayName = 'Sourcerer'; -export const Sourcerer = SOURCERER_FEATURE_FLAG_ON ? MaybeSourcerer : () => null; +export const Sourcerer = SOURCERER_FEATURE_FLAG_ON ? SourcererComponent : () => null; diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/selectors.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/selectors.tsx new file mode 100644 index 00000000000000..6bbe24e9218801 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/selectors.tsx @@ -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 { State } from '../../store'; +import { sourcererSelectors } from '../../store/sourcerer'; +import { KibanaIndexPatterns, ManageScope, SourcererScopeName } from '../../store/sourcerer/model'; + +export interface SourcererScopeSelector { + configIndexPatterns: string[]; + kibanaIndexPatterns: KibanaIndexPatterns; + sourcererScope: ManageScope; +} + +export const getSourcererScopeSelector = () => { + const getKibanaIndexPatternsSelector = sourcererSelectors.kibanaIndexPatternsSelector(); + const getScopesSelector = sourcererSelectors.scopesSelector(); + const getConfigIndexPatternsSelector = sourcererSelectors.configIndexPatternsSelector(); + + const mapStateToProps = (state: State, scopeId: SourcererScopeName): SourcererScopeSelector => { + const kibanaIndexPatterns = getKibanaIndexPatternsSelector(state); + const scope = getScopesSelector(state)[scopeId]; + const configIndexPatterns = getConfigIndexPatternsSelector(state); + + return { + kibanaIndexPatterns, + configIndexPatterns, + sourcererScope: scope, + }; + }; + + return mapStateToProps; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/translations.ts b/x-pack/plugins/security_solution/public/common/components/sourcerer/translations.ts index 71b1734dad6a65..473eb43d5c4fed 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/translations.ts @@ -6,23 +6,26 @@ import { i18n } from '@kbn/i18n'; -export const SOURCERER = i18n.translate('xpack.securitySolution.indexPatterns.sourcerer', { - defaultMessage: 'Sourcerer', +export const SOURCERER = i18n.translate('xpack.securitySolution.indexPatterns.dataSourcesLabel', { + defaultMessage: 'Data sources', }); -export const CHANGE_INDEX_PATTERNS = i18n.translate('xpack.securitySolution.indexPatterns.help', { - defaultMessage: 'Change index patterns', +export const ALL_DEFAULT = i18n.translate('xpack.securitySolution.indexPatterns.allDefault', { + defaultMessage: 'All default', }); -export const ADD_INDEX_PATTERNS = i18n.translate('xpack.securitySolution.indexPatterns.add', { - defaultMessage: 'Configure Kibana index patterns', +export const SELECT_INDEX_PATTERNS = i18n.translate('xpack.securitySolution.indexPatterns.help', { + defaultMessage: 'Data sources selection', }); -export const CONFIGURE_INDEX_PATTERNS = i18n.translate( - 'xpack.securitySolution.indexPatterns.configure', +export const SAVE_INDEX_PATTERNS = i18n.translate('xpack.securitySolution.indexPatterns.save', { + defaultMessage: 'Save', +}); + +export const INDEX_PATTERNS_SELECTION_LABEL = i18n.translate( + 'xpack.securitySolution.indexPatterns.selectionLabel', { - defaultMessage: - 'Configure additional Kibana index patterns to see them become available in the Security Solution', + defaultMessage: 'Choose the source of the data on this page', } ); @@ -33,3 +36,17 @@ export const DISABLED_INDEX_PATTERNS = i18n.translate( 'Disabled index patterns are recommended on this page, but first need to be configured in your Kibana index pattern settings', } ); + +export const INDEX_PATTERNS_RESET = i18n.translate( + 'xpack.securitySolution.indexPatterns.resetButton', + { + defaultMessage: 'Reset', + } +); + +export const PICK_INDEX_PATTERNS = i18n.translate( + 'xpack.securitySolution.indexPatterns.pickIndexPatternsCombo', + { + defaultMessage: 'Pick index patterns', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/helpers.ts b/x-pack/plugins/security_solution/public/common/components/top_n/helpers.ts index b654eaf17b47be..79cbd87cda2011 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/top_n/helpers.ts @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EventType } from '../../../timelines/store/timeline/model'; +import { TimelineEventsType } from '../../../../common/types/timeline'; import * as i18n from './translations'; export interface TopNOption { inputDisplay: string; - value: EventType; + value: TimelineEventsType; 'data-test-subj': string; } @@ -52,8 +52,8 @@ export const defaultOptions = [...rawEvents, ...alertEvents]; * is always in sync with the `EventType` chosen by the user in * the active timeline. */ -export const getOptions = (activeTimelineEventType?: EventType): TopNOption[] => { - switch (activeTimelineEventType) { +export const getOptions = (activeTimelineEventsType?: TimelineEventsType): TopNOption[] => { + switch (activeTimelineEventsType) { case 'all': return allEvents; case 'raw': diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx index 31318122eb5644..594bffbd4ff63e 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx @@ -168,6 +168,17 @@ const store = createStore( storage ); +let testProps = { + browserFields: mockBrowserFields, + field, + indexNames: [], + indexPattern: mockIndexPattern, + timelineId: TimelineId.hostsPageExternalAlerts, + toggleTopN: jest.fn(), + onFilterAdded: jest.fn(), + value, +}; + describe('StatefulTopN', () => { // Suppress warnings about "react-beautiful-dnd" /* eslint-disable no-console */ @@ -189,16 +200,7 @@ describe('StatefulTopN', () => { wrapper = mount( - + ); @@ -277,19 +279,14 @@ describe('StatefulTopN', () => { filterManager, }, }; + testProps = { + ...testProps, + timelineId: TimelineId.active, + }; wrapper = mount( - + ); @@ -345,37 +342,33 @@ describe('StatefulTopN', () => { expect(props.to).toEqual('2020-04-15T03:46:09.047Z'); }); }); + describe('rendering in a NON-active timeline context', () => { + test(`defaults to the 'Alert events' option when rendering in a NON-active timeline context (e.g. the Alerts table on the Detections page) when 'documentType' from 'useTimelineTypeContext()' is 'alerts'`, () => { + const filterManager = new FilterManager(mockUiSettingsForFilterManager); - test(`defaults to the 'Alert events' option when rendering in a NON-active timeline context (e.g. the Alerts table on the Detections page) when 'documentType' from 'useTimelineTypeContext()' is 'alerts'`, () => { - const filterManager = new FilterManager(mockUiSettingsForFilterManager); + const manageTimelineForTesting = { + [TimelineId.active]: { + ...getTimelineDefaults(TimelineId.active), + filterManager, + documentType: 'alerts', + }, + }; - const manageTimelineForTesting = { - [TimelineId.active]: { - ...getTimelineDefaults(TimelineId.active), - filterManager, - documentType: 'alerts', - }, - }; - - const wrapper = mount( - - - - - - ); - - const props = wrapper.find('[data-test-subj="top-n"]').first().props() as Props; - - expect(props.defaultView).toEqual('alert'); + testProps = { + ...testProps, + timelineId: TimelineId.detectionsPage, + }; + const wrapper = mount( + + + + + + ); + + const props = wrapper.find('[data-test-subj="top-n"]').first().props() as Props; + + expect(props.defaultView).toEqual('alert'); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx index d71242329bcda9..9c81cb57335a58 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx @@ -74,7 +74,7 @@ interface OwnProps { browserFields: BrowserFields; field: string; indexPattern: IIndexPattern; - indexToAdd: string[] | null; + indexNames: string[]; timelineId?: string; toggleTopN: () => void; onFilterAdded?: () => void; @@ -93,7 +93,7 @@ const StatefulTopNComponent: React.FC = ({ dataProviders, field, indexPattern, - indexToAdd, + indexNames, globalFilters = EMPTY_FILTERS, globalQuery = EMPTY_QUERY, kqlMode, @@ -109,7 +109,6 @@ const StatefulTopNComponent: React.FC = ({ const options = getOptions( timelineId === TimelineId.active ? activeTimelineEventType : undefined ); - return ( = ({ filters={timelineId === TimelineId.active ? EMPTY_FILTERS : globalFilters} from={timelineId === TimelineId.active ? activeTimelineFrom : from} indexPattern={indexPattern} - indexToAdd={indexToAdd} + indexNames={indexNames} options={options} query={timelineId === TimelineId.active ? EMPTY_QUERY : globalQuery} setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker} diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx index 667d1816e8f079..829f918ddfe1b0 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx @@ -13,6 +13,8 @@ import { setAbsoluteRangeDatePicker } from '../../store/inputs/actions'; import { allEvents, defaultOptions } from './helpers'; import { TopN } from './top_n'; +import { TimelineEventsType } from '../../../../common/types/timeline'; +import { InputsModelId } from '../../store/inputs/constants'; jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); @@ -103,29 +105,34 @@ describe('TopN', () => { const query = { query: '', language: 'kuery' }; + const toggleTopN = jest.fn(); + const eventTypes: { [id: string]: TimelineEventsType } = { + raw: 'raw', + alert: 'alert', + all: 'all', + }; + let testProps = { + defaultView: eventTypes.raw, + field, + filters: [], + from: '2020-04-14T00:31:47.695Z', + indexNames: [], + indexPattern: mockIndexPattern, + options: defaultOptions, + query, + setAbsoluteRangeDatePicker, + setAbsoluteRangeDatePickerTarget: 'global' as InputsModelId, + setQuery: jest.fn(), + to: '2020-04-15T00:31:47.695Z', + toggleTopN, + value, + }; describe('common functionality', () => { - let toggleTopN: () => void; let wrapper: ReactWrapper; - beforeEach(() => { - toggleTopN = jest.fn(); wrapper = mount( - + ); }); @@ -143,28 +150,12 @@ describe('TopN', () => { }); describe('events view', () => { - let toggleTopN: () => void; let wrapper: ReactWrapper; beforeEach(() => { - toggleTopN = jest.fn(); wrapper = mount( - + ); }); @@ -181,37 +172,25 @@ describe('TopN', () => { }); describe('alerts view', () => { - let toggleTopN: () => void; let wrapper: ReactWrapper; beforeEach(() => { - toggleTopN = jest.fn(); + testProps = { + ...testProps, + defaultView: eventTypes.alert, + }; wrapper = mount( - + ); }); - test(`it renders SignalsByCategory when defaultView is 'signal'`, () => { + test(`it renders SignalsByCategory when defaultView is 'alert'`, () => { expect(wrapper.find('[data-test-subj="alerts-histogram-panel"]').exists()).toBe(true); }); - test(`it does NOT render EventsByDataset when defaultView is 'signal'`, () => { + test(`it does NOT render EventsByDataset when defaultView is 'alert'`, () => { expect( wrapper.find('[data-test-subj="eventsByDatasetOverview-uuid.v4()Panel"]').exists() ).toBe(false); @@ -222,24 +201,14 @@ describe('TopN', () => { let wrapper: ReactWrapper; beforeEach(() => { + testProps = { + ...testProps, + defaultView: eventTypes.all, + options: allEvents, + }; wrapper = mount( - + ); }); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.tsx index 064241a7216f46..4f0a71dcc3ebba 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.tsx @@ -14,7 +14,7 @@ import { EventsByDataset } from '../../../overview/components/events_by_dataset' import { SignalsByCategory } from '../../../overview/components/signals_by_category'; import { Filter, IIndexPattern, Query } from '../../../../../../../src/plugins/data/public'; import { InputsModelId } from '../../store/inputs/constants'; -import { EventType } from '../../../timelines/store/timeline/model'; +import { TimelineEventsType } from '../../../../common/types/timeline'; import { TopNOption } from './helpers'; import * as i18n from './translations'; @@ -45,11 +45,11 @@ const TopNContent = styled.div` export interface Props extends Pick { combinedQueries?: string; - defaultView: EventType; + defaultView: TimelineEventsType; field: string; filters: Filter[]; indexPattern: IIndexPattern; - indexToAdd?: string[] | null; + indexNames: string[]; options: TopNOption[]; query: Query; setAbsoluteRangeDatePicker: ActionCreator<{ @@ -75,7 +75,7 @@ const TopNComponent: React.FC = ({ field, from, indexPattern, - indexToAdd, + indexNames, options, query = DEFAULT_QUERY, setAbsoluteRangeDatePicker, @@ -85,8 +85,10 @@ const TopNComponent: React.FC = ({ to, toggleTopN, }) => { - const [view, setView] = useState(defaultView); - const onViewSelected = useCallback((value: string) => setView(value as EventType), [setView]); + const [view, setView] = useState(defaultView); + const onViewSelected = useCallback((value: string) => setView(value as TimelineEventsType), [ + setView, + ]); useEffect(() => { setView(defaultView); @@ -123,7 +125,7 @@ const TopNComponent: React.FC = ({ from={from} headerChildren={headerChildren} indexPattern={indexPattern} - indexToAdd={indexToAdd} + indexNames={indexNames} onlyField={field} query={query} setAbsoluteRangeDatePickerTarget={setAbsoluteRangeDatePickerTarget} diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts b/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts index 5a4aec93dd9aaa..e5c09d229808b7 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts @@ -17,6 +17,7 @@ export enum CONSTANTS { networkPage = 'network.page', overviewPage = 'overview.page', savedQuery = 'savedQuery', + sourcerer = 'sourcerer', timeline = 'timeline', timelinePage = 'timeline.page', timerange = 'timerange', diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts b/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts index 6052913b4183b1..a915b1c9d09a7d 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts @@ -22,6 +22,8 @@ import { formatDate } from '../super_date_picker'; import { NavTab } from '../navigation/types'; import { CONSTANTS, UrlStateType } from './constants'; import { ReplaceStateInLocation, UpdateUrlStateString } from './types'; +import { sourcererSelectors } from '../../store/sourcerer'; +import { SourcererScopeName, SourcererScopePatterns } from '../../store/sourcerer/model'; export const decodeRisonUrlState = (value: string | undefined): T | null => { try { @@ -118,6 +120,7 @@ export const makeMapStateToProps = () => { const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); const getGlobalSavedQuerySelector = inputsSelectors.globalSavedQuerySelector(); const getTimeline = timelineSelectors.getTimelineByIdSelector(); + const getSourcererScopes = sourcererSelectors.scopesSelector(); const mapStateToProps = (state: State) => { const inputState = getInputsSelector(state); const { linkTo: globalLinkTo, timerange: globalTimerange } = inputState.global; @@ -147,10 +150,16 @@ export const makeMapStateToProps = () => { [CONSTANTS.savedQuery]: savedQuery.id, }; } + const sourcerer = getSourcererScopes(state); + const activeScopes: SourcererScopeName[] = Object.keys(sourcerer) as SourcererScopeName[]; + const selectedPatterns: SourcererScopePatterns = activeScopes + .filter((scope) => scope === SourcererScopeName.default) + .reduce((acc, scope) => ({ ...acc, [scope]: sourcerer[scope]?.selectedPatterns }), {}); return { urlState: { ...searchAttr, + [CONSTANTS.sourcerer]: selectedPatterns, [CONSTANTS.timerange]: { global: { [CONSTANTS.timerange]: globalTimerange, @@ -217,6 +226,17 @@ export const updateUrlStateString = ({ urlStateKey: urlKey, }); } + } else if (urlKey === CONSTANTS.sourcerer) { + const sourcererState = decodeRisonUrlState(newUrlStateString); + if (sourcererState != null && Object.keys(sourcererState).length > 0) { + return replaceStateInLocation({ + history, + pathName, + search, + urlStateToReplace: sourcererState, + urlStateKey: urlKey, + }); + } } else if (urlKey === CONSTANTS.filters) { const queryState = decodeRisonUrlState(newUrlStateString); if (isEmpty(queryState)) { diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx index 72df9d613abacb..fc970c066e8a5c 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx @@ -161,7 +161,7 @@ describe('UrlStateContainer', () => { ).toEqual({ hash: '', pathname: examplePath, - search: `?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, + search: `?query=(language:kuery,query:'host.name:%22siem-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, state: '', }); } diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx index 723f2d235864fe..9e845ec538aa0f 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx @@ -83,7 +83,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - "?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))", + "?query=(language:kuery,query:'host.name:%22siem-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))", state: '', }); }); @@ -114,7 +114,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - "?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "?query=(language:kuery,query:'host.name:%22siem-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", state: '', }); }); @@ -147,7 +147,40 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - "?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))&timeline=(id:hello_timeline_id,isOpen:!t)", + "?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))&timeline=(id:hello_timeline_id,isOpen:!t)", + state: '', + }); + }); + + test('sourcerer redux state updates the url', () => { + mockProps = getMockPropsObj({ + page: CONSTANTS.networkPage, + examplePath: '/network', + namespaceLower: 'network', + pageName: SecurityPageName.network, + detailName: undefined, + }).noSearch.undefinedQuery; + + const wrapper = mount( + useUrlStateHooks(args)} /> + ); + const newUrlState = { + ...mockProps.urlState, + sourcerer: ['cool', 'patterns'], + }; + + wrapper.setProps({ + hookProps: { ...mockProps, urlState: newUrlState, isInitializing: false }, + }); + wrapper.update(); + + expect( + mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0] + ).toStrictEqual({ + hash: '', + pathname: '/network', + search: + "?sourcerer=!(cool,patterns)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", state: '', }); }); @@ -176,7 +209,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: examplePath, search: - "?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", state: '', }); } @@ -204,7 +237,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => expect( mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0].search ).toEqual( - "?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))" + "?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))" ); wrapper.setProps({ hookProps: updatedProps }); @@ -213,7 +246,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => expect( mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0].search ).toEqual( - "?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))" + "?query=(language:kuery,query:'host.name:%22siem-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))" ); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx index 6eccf52ec72da2..1e77ae77666301 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx @@ -8,7 +8,7 @@ import { get, isEmpty } from 'lodash/fp'; import { Dispatch } from 'redux'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; -import { inputsActions } from '../../store/actions'; +import { inputsActions, sourcererActions } from '../../store/actions'; import { InputsModelId, TimeRangeKinds } from '../../store/inputs/constants'; import { UrlInputsModel, @@ -22,6 +22,8 @@ import { decodeRisonUrlState } from './helpers'; import { normalizeTimeRange } from './normalize_time_range'; import { DispatchSetInitialStateFromUrl, SetInitialStateFromUrl } from './types'; import { queryTimelineById } from '../../../timelines/components/open_timeline/helpers'; +import { SourcererScopeName, SourcererScopePatterns } from '../../store/sourcerer/model'; +import { SecurityPageName } from '../../../../common/constants'; export const dispatchSetInitialStateFromUrl = ( dispatch: Dispatch @@ -40,6 +42,22 @@ export const dispatchSetInitialStateFromUrl = ( if (urlKey === CONSTANTS.timerange) { updateTimerange(newUrlStateString, dispatch); } + if (urlKey === CONSTANTS.sourcerer) { + const sourcererState = decodeRisonUrlState(newUrlStateString); + if (sourcererState != null) { + const activeScopes: SourcererScopeName[] = Object.keys(sourcererState).filter( + (key) => !(key === SourcererScopeName.default && pageName === SecurityPageName.detections) + ) as SourcererScopeName[]; + activeScopes.forEach((scope) => + dispatch( + sourcererActions.setSelectedIndexPatterns({ + id: scope, + selectedPatterns: sourcererState[scope] ?? [], + }) + ) + ); + } + } if (urlKey === CONSTANTS.appQuery && indexPattern != null) { const appQuery = decodeRisonUrlState(newUrlStateString); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts b/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts index 8d471e843320c2..6f04226fa3a198 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts @@ -117,6 +117,7 @@ export const defaultProps: UrlStateContainerPropTypes = { id: '', isOpen: false, }, + [CONSTANTS.sourcerer]: {}, }, setInitialStateFromUrl: dispatchSetInitialStateFromUrl(mockDispatch), updateTimeline: (jest.fn() as unknown) as DispatchUpdateTimeline, diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/types.ts b/x-pack/plugins/security_solution/public/common/components/url_state/types.ts index f383e181323854..301771a4db6b90 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/types.ts @@ -22,11 +22,13 @@ import { DispatchUpdateTimeline } from '../../../timelines/components/open_timel import { NavTab } from '../navigation/types'; import { CONSTANTS, UrlStateType } from './constants'; +import { SourcererScopePatterns } from '../../store/sourcerer/model'; export const ALL_URL_STATE_KEYS: KeyUrlState[] = [ CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ]; @@ -36,6 +38,7 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ], @@ -43,6 +46,7 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ], @@ -51,6 +55,7 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ], @@ -58,6 +63,7 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ], @@ -65,6 +71,7 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ], @@ -72,6 +79,7 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ], @@ -93,6 +101,7 @@ export interface UrlState { [CONSTANTS.appQuery]?: Query; [CONSTANTS.filters]?: Filter[]; [CONSTANTS.savedQuery]?: string; + [CONSTANTS.sourcerer]: SourcererScopePatterns; [CONSTANTS.timerange]: UrlInputsModel; [CONSTANTS.timeline]: TimelineUrl; } diff --git a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx index f6ebbb990f2234..489ccb23c9b2c4 100644 --- a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx @@ -29,6 +29,7 @@ const AnomaliesQueryTabBodyComponent: React.FC = ({ AnomaliesTableComponent, flowTarget, ip, + indexNames, }) => { const { jobs } = useInstalledSecurityJobs(); const [anomalyScore] = useUiSetting$(DEFAULT_ANOMALY_SCORE); @@ -57,6 +58,7 @@ const AnomaliesQueryTabBodyComponent: React.FC = ({ endDate={endDate} filterQuery={mergedFilterQuery} id={ID} + indexNames={indexNames} setQuery={setQuery} startDate={startDate} {...histogramConfigs} diff --git a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts index d716df70246f78..3ce4b8b6d4494a 100644 --- a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts +++ b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts @@ -24,6 +24,7 @@ export type AnomaliesQueryTabBodyProps = QueryTabBodyProps & { deleteQuery?: ({ id }: { id: string }) => void; endDate: GlobalTimeArgs['to']; flowTarget?: FlowTarget; + indexNames: string[]; narrowDateRange: NarrowDateRange; setQuery: GlobalTimeArgs['setQuery']; startDate: GlobalTimeArgs['from']; diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts index 3d79c83dc42cb9..dc2d6605bc292d 100644 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts @@ -8,7 +8,6 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel } from '../../../../common/store'; import { useKibana } from '../../../../common/lib/kibana'; import { @@ -23,10 +22,10 @@ import { isCompleteResponse, isErrorResponse, } from '../../../../../../../../src/plugins/data/common'; -import { useWithSource } from '../../source'; import * as i18n from './translations'; +import { DocValueFields } from '../../../../../common/search_strategy'; -// const ID = 'timelineEventsLastEventTimeQuery'; +const ID = 'timelineEventsLastEventTimeQuery'; export interface UseTimelineLastEventTimeArgs { lastSeen: string | null; @@ -35,26 +34,29 @@ export interface UseTimelineLastEventTimeArgs { } interface UseTimelineLastEventTimeProps { + docValueFields: DocValueFields[]; indexKey: LastEventIndexKey; + indexNames: string[]; details: LastTimeDetails; } export const useTimelineLastEventTime = ({ + docValueFields, indexKey, + indexNames, details, }: UseTimelineLastEventTimeProps): [boolean, UseTimelineLastEventTimeArgs] => { - const { data, notifications, uiSettings } = useKibana().services; - const { docValueFields } = useWithSource('default'); + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [TimelineLastEventTimeRequest, setTimelineLastEventTimeRequest] = useState< TimelineEventsLastEventTimeRequestOptions >({ - defaultIndex, - factoryQueryType: TimelineEventsQueries.lastEventTime, + defaultIndex: indexNames, docValueFields, + factoryQueryType: TimelineEventsQueries.lastEventTime, + id: ID, indexKey, details, }); @@ -133,7 +135,8 @@ export const useTimelineLastEventTime = ({ setTimelineLastEventTimeRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, + docValueFields, indexKey, details, }; @@ -142,7 +145,7 @@ export const useTimelineLastEventTime = ({ } return prevRequest; }); - }, [defaultIndex, details, indexKey]); + }, [indexNames, details, docValueFields, indexKey]); useEffect(() => { timelineLastEventTimeSearch(TimelineLastEventTimeRequest); diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts index 8e0c133f95b4d3..ca8bcc637717b4 100644 --- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts @@ -5,14 +5,13 @@ */ import deepEqual from 'fast-deep-equal'; -import { isEmpty, noop } from 'lodash/fp'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { noop } from 'lodash/fp'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { MatrixHistogramQueryProps } from '../../components/matrix_histogram/types'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel } from '../../../common/store'; import { createFilter } from '../../../common/containers/helpers'; -import { useKibana, useUiSetting$ } from '../../../common/lib/kibana'; +import { useKibana } from '../../../common/lib/kibana'; import { MatrixHistogramQuery, MatrixHistogramRequestOptions, @@ -40,25 +39,18 @@ export const useMatrixHistogram = ({ errorMessage, filterQuery, histogramType, - indexToAdd, + indexNames, stackByField, startDate, }: MatrixHistogramQueryProps): [boolean, UseMatrixHistogramArgs] => { const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const [configIndex] = useUiSetting$(DEFAULT_INDEX_KEY); - const defaultIndex = useMemo(() => { - if (indexToAdd != null && !isEmpty(indexToAdd)) { - return [...configIndex, ...indexToAdd]; - } - return configIndex; - }, [configIndex, indexToAdd]); const [loading, setLoading] = useState(false); const [matrixHistogramRequest, setMatrixHistogramRequest] = useState< MatrixHistogramRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: MatrixHistogramQuery, filterQuery: createFilter(filterQuery), histogramType, @@ -140,7 +132,7 @@ export const useMatrixHistogram = ({ setMatrixHistogramRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), timerange: { interval: '12h', @@ -153,7 +145,7 @@ export const useMatrixHistogram = ({ } return prevRequest; }); - }, [defaultIndex, endDate, filterQuery, startDate]); + }, [indexNames, endDate, filterQuery, startDate]); useEffect(() => { hostsSearch(matrixHistogramRequest); diff --git a/x-pack/plugins/security_solution/public/common/containers/query_template.tsx b/x-pack/plugins/security_solution/public/common/containers/query_template.tsx index eaa43c255a944a..80791d91481a8a 100644 --- a/x-pack/plugins/security_solution/public/common/containers/query_template.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/query_template.tsx @@ -14,6 +14,7 @@ import { DocValueFields } from './source'; export { DocValueFields }; export interface QueryTemplateProps { + indexNames: string[]; docValueFields?: DocValueFields[]; id?: string; endDate?: string; diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.gql_query.ts b/x-pack/plugins/security_solution/public/common/containers/source/index.gql_query.ts deleted file mode 100644 index 630515c5cbed48..00000000000000 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.gql_query.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -export const sourceQuery = gql` - query SourceQuery($sourceId: ID = "default", $defaultIndex: [String!]!) { - source(id: $sourceId) { - id - status { - indicesExist(defaultIndex: $defaultIndex) - indexFields(defaultIndex: $defaultIndex) { - category - description - example - indexes - name - searchable - type - aggregatable - format - esTypes - subType - } - } - } - } -`; diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx b/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx deleted file mode 100644 index 8ba7f7da7b8e37..00000000000000 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { act, renderHook } from '@testing-library/react-hooks'; - -import { useWithSource, indicesExistOrDataTemporarilyUnavailable } from '.'; -import { NO_ALERT_INDEX } from '../../../../common/constants'; -import { mockBrowserFields, mockIndexFields, mocksSource } from './mock'; - -jest.mock('../../lib/kibana'); -jest.mock('../../utils/apollo_context', () => ({ - useApolloClient: jest.fn().mockReturnValue({ - query: jest.fn().mockImplementation(() => Promise.resolve(mocksSource[0].result)), - }), -})); - -describe('Index Fields & Browser Fields', () => { - test('At initialization the value of indicesExists should be true', async () => { - const { result, waitForNextUpdate } = renderHook(() => useWithSource()); - const initialResult = result.current; - - await waitForNextUpdate(); - - return expect(initialResult).toEqual({ - browserFields: {}, - docValueFields: [], - errorMessage: null, - indexPattern: { - fields: [], - title: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', - }, - indicesExist: true, - loading: true, - }); - }); - - test('returns memoized value', async () => { - const { result, waitForNextUpdate, rerender } = renderHook(() => useWithSource()); - await waitForNextUpdate(); - - const result1 = result.current; - act(() => rerender()); - const result2 = result.current; - - return expect(result1).toBe(result2); - }); - - test('Index Fields', async () => { - const { result, waitForNextUpdate } = renderHook(() => useWithSource()); - - await waitForNextUpdate(); - - return expect(result).toEqual({ - current: { - indicesExist: true, - browserFields: mockBrowserFields, - docValueFields: [ - { - field: '@timestamp', - format: 'date_time', - }, - { - field: 'event.end', - format: 'date_time', - }, - ], - indexPattern: { - fields: mockIndexFields, - title: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', - }, - loading: false, - errorMessage: null, - }, - error: undefined, - }); - }); - - test('Make sure we are not querying for NO_ALERT_INDEX and it is not includes in the index pattern', async () => { - const { result, waitForNextUpdate } = renderHook(() => - useWithSource('default', [NO_ALERT_INDEX]) - ); - - await waitForNextUpdate(); - return expect(result.current.indexPattern.title).toEqual( - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*' - ); - }); - - describe('indicesExistOrDataTemporarilyUnavailable', () => { - test('it returns true when undefined', () => { - let undefVar; - const result = indicesExistOrDataTemporarilyUnavailable(undefVar); - expect(result).toBeTruthy(); - }); - test('it returns true when true', () => { - const result = indicesExistOrDataTemporarilyUnavailable(true); - expect(result).toBeTruthy(); - }); - test('it returns false when false', () => { - const result = indicesExistOrDataTemporarilyUnavailable(false); - expect(result).toBeFalsy(); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx index ffbecf9e3d4334..4b1db8a2871bd8 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx @@ -4,42 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ -import { isUndefined } from 'lodash'; import { set } from '@elastic/safer-lodash-set/fp'; -import { get, keyBy, pick, isEmpty } from 'lodash/fp'; -import { useEffect, useMemo, useState } from 'react'; +import { keyBy, pick, isEmpty, isEqual, isUndefined } from 'lodash/fp'; import memoizeOne from 'memoize-one'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useDispatch, useSelector, shallowEqual } from 'react-redux'; import { IIndexPattern } from 'src/plugins/data/public'; -import { DEFAULT_INDEX_KEY, NO_ALERT_INDEX } from '../../../../common/constants'; -import { useUiSetting$ } from '../../lib/kibana'; +import { useKibana } from '../../lib/kibana'; +import { + IndexField, + IndexFieldsStrategyResponse, + IndexFieldsStrategyRequest, + BrowserField, + BrowserFields, +} from '../../../../common/search_strategy/index_fields'; +import { AbortError } from '../../../../../../../src/plugins/data/common'; +import * as i18n from './translations'; +import { SourcererScopeName } from '../../store/sourcerer/model'; +import { sourcererActions, sourcererSelectors } from '../../store/sourcerer'; -import { IndexField, SourceQuery } from '../../../graphql/types'; +import { State } from '../../store'; +import { DocValueFields } from '../../../../common/search_strategy/common'; -import { sourceQuery } from './index.gql_query'; -import { useApolloClient } from '../../utils/apollo_context'; - -export { sourceQuery }; - -export interface BrowserField { - aggregatable: boolean; - category: string; - description: string | null; - example: string | number | null; - fields: Readonly>>; - format: string; - indexes: string[]; - name: string; - searchable: boolean; - type: string; -} - -export interface DocValueFields { - field: string; - format: string; -} - -export type BrowserFields = Readonly>>; +export { BrowserField, BrowserFields, DocValueFields }; export const getAllBrowserFields = (browserFields: BrowserFields): Array> => Object.values(browserFields).reduce>>( @@ -85,14 +73,12 @@ export const getDocValueFields = memoizeOne( (_title: string, fields: IndexField[]): DocValueFields[] => fields && fields.length > 0 ? fields.reduce((accumulator: DocValueFields[], field: IndexField) => { - if (field.type === 'date' && accumulator.length < 100) { - const format: string = - field.format != null && !isEmpty(field.format) ? field.format : 'date_time'; + if (field.readFromDocValues && accumulator.length < 100) { return [ ...accumulator, { field: field.name, - format, + format: field.format, }, ]; } @@ -107,115 +93,196 @@ export const indicesExistOrDataTemporarilyUnavailable = ( indicesExist: boolean | null | undefined ) => indicesExist || isUndefined(indicesExist); -const EMPTY_BROWSER_FIELDS = {}; -const EMPTY_DOCVALUE_FIELD: DocValueFields[] = []; +const DEFAULT_BROWSER_FIELDS = {}; +const DEFAULT_INDEX_PATTERNS = { fields: [], title: '' }; +const DEFAULT_DOC_VALUE_FIELDS: DocValueFields[] = []; -interface UseWithSourceState { +interface FetchIndexReturn { browserFields: BrowserFields; docValueFields: DocValueFields[]; - errorMessage: string | null; - indexPattern: IIndexPattern; - indicesExist: boolean | undefined | null; - loading: boolean; + indexes: string[]; + indexExists: boolean; + indexPatterns: IIndexPattern; } -export const useWithSource = ( - sourceId = 'default', - indexToAdd?: string[] | null, - onlyCheckIndexToAdd?: boolean, - // Fun fact: When using this hook multiple times within a component (e.g. add_exception_modal & edit_exception_modal), - // the apolloClient will perform queryDeduplication and prevent the first query from executing. A deep compare is not - // performed on `indices`, so another field must be passed to circumvent this. - // For details, see https://github.com/apollographql/react-apollo/issues/2202 - queryDeduplication = 'default' -) => { - const [configIndex] = useUiSetting$(DEFAULT_INDEX_KEY); - const defaultIndex = useMemo(() => { - const filterIndexAdd = (indexToAdd ?? []).filter((item) => item !== NO_ALERT_INDEX); - if (!isEmpty(filterIndexAdd)) { - return onlyCheckIndexToAdd ? filterIndexAdd : [...configIndex, ...filterIndexAdd]; - } - return configIndex; - }, [configIndex, indexToAdd, onlyCheckIndexToAdd]); - - const [state, setState] = useState({ - browserFields: EMPTY_BROWSER_FIELDS, - docValueFields: EMPTY_DOCVALUE_FIELD, - errorMessage: null, - indexPattern: getIndexFields(defaultIndex.join(), []), - indicesExist: indicesExistOrDataTemporarilyUnavailable(undefined), - loading: true, +export const useFetchIndex = ( + indexNames: string[], + onlyCheckIfIndicesExist: boolean = false +): [boolean, FetchIndexReturn] => { + const { data, notifications } = useKibana().services; + const abortCtrl = useRef(new AbortController()); + const previousIndexesName = useRef([]); + const [isLoading, setLoading] = useState(true); + + const [state, setState] = useState({ + browserFields: DEFAULT_BROWSER_FIELDS, + docValueFields: DEFAULT_DOC_VALUE_FIELDS, + indexes: indexNames, + indexExists: true, + indexPatterns: DEFAULT_INDEX_PATTERNS, }); - const apolloClient = useApolloClient(); + const indexFieldsSearch = useCallback( + (iNames) => { + let didCancel = false; + const asyncSearch = async () => { + abortCtrl.current = new AbortController(); + setLoading(true); + const searchSubscription$ = data.search + .search( + { indices: iNames, onlyCheckIfIndicesExist }, + { + abortSignal: abortCtrl.current.signal, + strategy: 'securitySolutionIndexFields', + } + ) + .subscribe({ + next: (response) => { + if (!response.isPartial && !response.isRunning) { + if (!didCancel) { + const stringifyIndices = response.indicesExist.sort().join(); + previousIndexesName.current = response.indicesExist; + setLoading(false); + setState({ + browserFields: getBrowserFields(stringifyIndices, response.indexFields), + docValueFields: getDocValueFields(stringifyIndices, response.indexFields), + indexes: response.indicesExist, + indexExists: response.indicesExist.length > 0, + indexPatterns: getIndexFields(stringifyIndices, response.indexFields), + }); + } + searchSubscription$.unsubscribe(); + } else if (!didCancel && response.isPartial && !response.isRunning) { + setLoading(false); + notifications.toasts.addWarning(i18n.ERROR_BEAT_FIELDS); + searchSubscription$.unsubscribe(); + } + }, + error: (msg) => { + if (!didCancel) { + setLoading(false); + } - useEffect(() => { - let isSubscribed = true; - const abortCtrl = new AbortController(); - - async function fetchSource() { - if (!apolloClient) return; - - setState((prevState) => ({ ...prevState, loading: true })); - - try { - const result = await apolloClient.query< - SourceQuery.Query, - SourceQuery.Variables & { queryDeduplication: string } - >({ - query: sourceQuery, - fetchPolicy: 'cache-first', - variables: { - sourceId, - defaultIndex, - queryDeduplication, - }, - context: { - fetchOptions: { - signal: abortCtrl.signal, + if (!(msg instanceof AbortError)) { + notifications.toasts.addDanger({ + text: msg.message, + title: i18n.FAIL_BEAT_FIELDS, + }); + } }, - }, - }); - - if (isSubscribed) { - setState({ - loading: false, - indicesExist: indicesExistOrDataTemporarilyUnavailable( - get('data.source.status.indicesExist', result) - ), - browserFields: getBrowserFields( - defaultIndex.join(), - get('data.source.status.indexFields', result) - ), - docValueFields: getDocValueFields( - defaultIndex.join(), - get('data.source.status.indexFields', result) - ), - indexPattern: getIndexFields( - defaultIndex.join(), - get('data.source.status.indexFields', result) - ), - errorMessage: null, }); - } - } catch (error) { - if (isSubscribed) { - setState((prevState) => ({ - ...prevState, - loading: false, - errorMessage: error.message, - })); - } - } + }; + abortCtrl.current.abort(); + asyncSearch(); + return () => { + didCancel = true; + abortCtrl.current.abort(); + }; + }, + [data.search, notifications.toasts, onlyCheckIfIndicesExist] + ); + + useEffect(() => { + if (!isEmpty(indexNames) && !isEqual(previousIndexesName.current, indexNames)) { + indexFieldsSearch(indexNames); } + }, [indexNames, indexFieldsSearch, previousIndexesName]); + + return [isLoading, state]; +}; + +export const useIndexFields = (sourcererScopeName: SourcererScopeName) => { + const { data, notifications } = useKibana().services; + const abortCtrl = useRef(new AbortController()); + const dispatch = useDispatch(); + const previousIndexesName = useRef([]); + + const indexNamesSelectedSelector = useMemo( + () => sourcererSelectors.getIndexNamesSelectedSelector(), + [] + ); + const indexNames = useSelector( + (state) => indexNamesSelectedSelector(state, sourcererScopeName), + shallowEqual + ); - fetchSource(); + const setLoading = useCallback( + (loading: boolean) => { + dispatch(sourcererActions.setSourcererScopeLoading({ id: sourcererScopeName, loading })); + }, + [dispatch, sourcererScopeName] + ); + + const indexFieldsSearch = useCallback( + (indicesName) => { + let didCancel = false; + const asyncSearch = async () => { + abortCtrl.current = new AbortController(); + setLoading(true); + const searchSubscription$ = data.search + .search( + { indices: indicesName, onlyCheckIfIndicesExist: false }, + { + abortSignal: abortCtrl.current.signal, + strategy: 'securitySolutionIndexFields', + } + ) + .subscribe({ + next: (response) => { + if (!response.isPartial && !response.isRunning) { + if (!didCancel) { + const stringifyIndices = response.indicesExist.sort().join(); + previousIndexesName.current = response.indicesExist; + dispatch( + sourcererActions.setSource({ + id: sourcererScopeName, + payload: { + browserFields: getBrowserFields(stringifyIndices, response.indexFields), + docValueFields: getDocValueFields(stringifyIndices, response.indexFields), + errorMessage: null, + id: sourcererScopeName, + indexPattern: getIndexFields(stringifyIndices, response.indexFields), + indicesExist: response.indicesExist.length > 0, + loading: false, + }, + }) + ); + } + searchSubscription$.unsubscribe(); + } else if (!didCancel && response.isPartial && !response.isRunning) { + // TODO: Make response error status clearer + setLoading(false); + notifications.toasts.addWarning(i18n.ERROR_BEAT_FIELDS); + searchSubscription$.unsubscribe(); + } + }, + error: (msg) => { + if (!didCancel) { + setLoading(false); + } - return () => { - isSubscribed = false; - return abortCtrl.abort(); - }; - }, [apolloClient, sourceId, defaultIndex, queryDeduplication]); + if (!(msg instanceof AbortError)) { + notifications.toasts.addDanger({ + text: msg.message, + title: i18n.FAIL_BEAT_FIELDS, + }); + } + }, + }); + }; + abortCtrl.current.abort(); + asyncSearch(); + return () => { + didCancel = true; + abortCtrl.current.abort(); + }; + }, + [data.search, dispatch, notifications.toasts, setLoading, sourcererScopeName] + ); - return state; + useEffect(() => { + if (!isEmpty(indexNames) && !isEqual(previousIndexesName.current, indexNames)) { + indexFieldsSearch(indexNames); + } + }, [indexNames, indexFieldsSearch, previousIndexesName]); }; diff --git a/x-pack/plugins/security_solution/public/common/containers/source/mock.ts b/x-pack/plugins/security_solution/public/common/containers/source/mock.ts index bba6a15d739709..7fcd11f71f081f 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/mock.ts +++ b/x-pack/plugins/security_solution/public/common/containers/source/mock.ts @@ -5,347 +5,296 @@ */ import { DEFAULT_INDEX_PATTERN } from '../../../../common/constants'; +import { DocValueFields } from '../../../../common/search_strategy'; +import { BrowserFields } from '../../../../common/search_strategy/index_fields'; -import { BrowserFields, DocValueFields } from '.'; -import { sourceQuery } from './index.gql_query'; - -export const mocksSource = [ - { - request: { - query: sourceQuery, - variables: { - sourceId: 'default', - defaultIndex: DEFAULT_INDEX_PATTERN, - }, +export const mocksSource = { + indexFields: [ + { + category: 'base', + description: + 'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.', + example: '2016-05-23T08:05:34.853Z', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: '@timestamp', + searchable: true, + type: 'date', + aggregatable: true, }, - result: { - data: { - source: { - id: 'default', - configuration: {}, - status: { - indicesExist: true, - winlogbeatIndices: [ - 'winlogbeat-7.0.0-2019.02.17', - 'winlogbeat-7.0.0-2019.02.18', - 'winlogbeat-7.0.0-2019.02.19', - 'winlogbeat-7.0.0-2019.02.20', - 'winlogbeat-7.0.0-2019.02.21', - 'winlogbeat-7.0.0-2019.02.21-000001', - 'winlogbeat-7.0.0-2019.02.22', - 'winlogbeat-8.0.0-2019.02.19-000001', - ], - auditbeatIndices: [ - 'auditbeat-7.0.0-2019.02.17', - 'auditbeat-7.0.0-2019.02.18', - 'auditbeat-7.0.0-2019.02.19', - 'auditbeat-7.0.0-2019.02.20', - 'auditbeat-7.0.0-2019.02.21', - 'auditbeat-7.0.0-2019.02.21-000001', - 'auditbeat-7.0.0-2019.02.22', - 'auditbeat-8.0.0-2019.02.19-000001', - ], - filebeatIndices: [ - 'filebeat-7.0.0-iot-2019.06', - 'filebeat-7.0.0-iot-2019.07', - 'filebeat-7.0.0-iot-2019.08', - 'filebeat-7.0.0-iot-2019.09', - 'filebeat-7.0.0-iot-2019.10', - 'filebeat-8.0.0-2019.02.19-000001', - ], - indexFields: [ - { - category: 'base', - description: - 'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.', - example: '2016-05-23T08:05:34.853Z', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: '@timestamp', - searchable: true, - type: 'date', - aggregatable: true, - }, - { - category: 'agent', - description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.ephemeral_id', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'agent', - description: null, - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.hostname', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'agent', - description: - 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', - example: '8a4f500d', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.id', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'agent', - description: - 'Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', - example: 'foo', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.name', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'auditd', - description: null, - example: null, - format: '', - indexes: ['auditbeat'], - name: 'auditd.data.a0', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'auditd', - description: null, - example: null, - format: '', - indexes: ['auditbeat'], - name: 'auditd.data.a1', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'auditd', - description: null, - example: null, - format: '', - indexes: ['auditbeat'], - name: 'auditd.data.a2', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'client', - description: - 'Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.address', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'client', - description: 'Bytes sent from the client to the server.', - example: '184', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.bytes', - searchable: true, - type: 'number', - aggregatable: true, - }, - { - category: 'client', - description: 'Client domain.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.domain', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'client', - description: 'Country ISO code.', - example: 'CA', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.geo.country_iso_code', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'cloud', - description: - 'The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.', - example: '666777888999', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'cloud.account.id', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'cloud', - description: 'Availability zone in which this host is running.', - example: 'us-east-1c', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'cloud.availability_zone', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'container', - description: 'Unique container id.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'container.id', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'container', - description: 'Name of the image the container was built on.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'container.image.name', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'container', - description: 'Container image tag.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'container.image.tag', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'destination', - description: - 'Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.address', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'destination', - description: 'Bytes sent from the destination to the source.', - example: '184', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.bytes', - searchable: true, - type: 'number', - aggregatable: true, - }, - { - category: 'destination', - description: 'Destination domain.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.domain', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - aggregatable: true, - category: 'destination', - description: - 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.ip', - searchable: true, - type: 'ip', - }, - { - aggregatable: true, - category: 'destination', - description: 'Port of the destination.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.port', - searchable: true, - type: 'long', - }, - { - aggregatable: true, - category: 'source', - description: - 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'source.ip', - searchable: true, - type: 'ip', - }, - { - aggregatable: true, - category: 'source', - description: 'Port of the source.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'source.port', - searchable: true, - type: 'long', - }, - { - aggregatable: true, - category: 'event', - description: - 'event.end contains the date when the event ended or when the activity was last observed.', - example: null, - format: '', - indexes: DEFAULT_INDEX_PATTERN, - name: 'event.end', - searchable: true, - type: 'date', - }, - ], - }, - }, - }, + { + category: 'agent', + description: + 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', + example: '8a4f500f', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'agent.ephemeral_id', + searchable: true, + type: 'string', + aggregatable: true, }, - }, -]; + { + category: 'agent', + description: null, + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'agent.hostname', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'agent', + description: + 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', + example: '8a4f500d', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'agent.id', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'agent', + description: + 'Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', + example: 'foo', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'agent.name', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'auditd', + description: null, + example: null, + format: '', + indexes: ['auditbeat'], + name: 'auditd.data.a0', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'auditd', + description: null, + example: null, + format: '', + indexes: ['auditbeat'], + name: 'auditd.data.a1', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'auditd', + description: null, + example: null, + format: '', + indexes: ['auditbeat'], + name: 'auditd.data.a2', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'client', + description: + 'Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'client.address', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'client', + description: 'Bytes sent from the client to the server.', + example: '184', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'client.bytes', + searchable: true, + type: 'number', + aggregatable: true, + }, + { + category: 'client', + description: 'Client domain.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'client.domain', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'client', + description: 'Country ISO code.', + example: 'CA', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'client.geo.country_iso_code', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'cloud', + description: + 'The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.', + example: '666777888999', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'cloud.account.id', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'cloud', + description: 'Availability zone in which this host is running.', + example: 'us-east-1c', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'cloud.availability_zone', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'container', + description: 'Unique container id.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'container.id', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'container', + description: 'Name of the image the container was built on.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'container.image.name', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'container', + description: 'Container image tag.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'container.image.tag', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'destination', + description: + 'Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.address', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'destination', + description: 'Bytes sent from the destination to the source.', + example: '184', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.bytes', + searchable: true, + type: 'number', + aggregatable: true, + }, + { + category: 'destination', + description: 'Destination domain.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.domain', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + aggregatable: true, + category: 'destination', + description: 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.ip', + searchable: true, + type: 'ip', + }, + { + aggregatable: true, + category: 'destination', + description: 'Port of the destination.', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.port', + searchable: true, + type: 'long', + }, + { + aggregatable: true, + category: 'source', + description: 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'source.ip', + searchable: true, + type: 'ip', + }, + { + aggregatable: true, + category: 'source', + description: 'Port of the source.', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'source.port', + searchable: true, + type: 'long', + }, + { + aggregatable: true, + category: 'event', + description: + 'event.end contains the date when the event ended or when the activity was last observed.', + example: null, + format: '', + indexes: DEFAULT_INDEX_PATTERN, + name: 'event.end', + searchable: true, + type: 'date', + }, + ], +}; export const mockIndexFields = [ { aggregatable: true, name: '@timestamp', searchable: true, type: 'date' }, diff --git a/x-pack/plugins/security_solution/public/common/containers/source/translations.ts b/x-pack/plugins/security_solution/public/common/containers/source/translations.ts new file mode 100644 index 00000000000000..f12a9a0b41a7be --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/containers/source/translations.ts @@ -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 { i18n } from '@kbn/i18n'; + +export const ERROR_BEAT_FIELDS = i18n.translate( + 'xpack.securitySolution.beatFields.errorSearchDescription', + { + defaultMessage: `An error has occurred on getting beat fields`, + } +); + +export const FAIL_BEAT_FIELDS = i18n.translate( + 'xpack.securitySolution.beatFields.failSearchDescription', + { + defaultMessage: `Failed to run search on beat fields`, + } +); diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/constants.ts b/x-pack/plugins/security_solution/public/common/containers/sourcerer/constants.ts index 106294ba54f5a2..be3d074811032d 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/constants.ts +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/constants.ts @@ -4,26 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const SOURCERER_FEATURE_FLAG_ON = false; - -export enum SecurityPageName { - default = 'default', - host = 'host', - detections = 'detections', - timeline = 'timeline', - network = 'network', -} - -export type SourceGroupsType = keyof typeof SecurityPageName; - -export const sourceGroups = { - [SecurityPageName.default]: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'winlogbeat-*', - 'blobbeat-*', - ], -}; +export const SOURCERER_FEATURE_FLAG_ON = true; diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/format.test.tsx b/x-pack/plugins/security_solution/public/common/containers/sourcerer/format.test.tsx deleted file mode 100644 index b8017df09b7380..00000000000000 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/format.test.tsx +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { indicesExistOrDataTemporarilyUnavailable } from './format'; - -describe('indicesExistOrDataTemporarilyUnavailable', () => { - it('it returns true when undefined', () => { - let undefVar; - const result = indicesExistOrDataTemporarilyUnavailable(undefVar); - expect(result).toBeTruthy(); - }); - it('it returns true when true', () => { - const result = indicesExistOrDataTemporarilyUnavailable(true); - expect(result).toBeTruthy(); - }); - it('it returns false when false', () => { - const result = indicesExistOrDataTemporarilyUnavailable(false); - expect(result).toBeFalsy(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/format.ts b/x-pack/plugins/security_solution/public/common/containers/sourcerer/format.ts deleted file mode 100644 index 8c9a16ed705ef6..00000000000000 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/format.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { isEmpty, pick } from 'lodash/fp'; -import memoizeOne from 'memoize-one'; -import { set } from '@elastic/safer-lodash-set/fp'; -import { isUndefined } from 'lodash'; -import { IndexField } from '../../../graphql/types'; -import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; - -export interface BrowserField { - aggregatable: boolean; - category: string; - description: string | null; - example: string | number | null; - fields: Readonly>>; - format: string; - indexes: string[]; - name: string; - searchable: boolean; - type: string; -} - -export interface DocValueFields { - field: string; - format: string; -} - -export type BrowserFields = Readonly>>; - -export const getAllBrowserFields = (browserFields: BrowserFields): Array> => - Object.values(browserFields).reduce>>( - (acc, namespace) => [ - ...acc, - ...Object.values(namespace.fields != null ? namespace.fields : {}), - ], - [] - ); - -export const getIndexFields = memoizeOne( - (title: string, fields: IndexField[]): IIndexPattern => - fields && fields.length > 0 - ? { - fields: fields.map((field) => - pick(['name', 'searchable', 'type', 'aggregatable', 'esTypes', 'subType'], field) - ), - title, - } - : { fields: [], title }, - (newArgs, lastArgs) => newArgs[0] === lastArgs[0] && newArgs[1].length === lastArgs[1].length -); - -export const getBrowserFields = memoizeOne( - (_title: string, fields: IndexField[]): BrowserFields => - fields && fields.length > 0 - ? fields.reduce( - (accumulator: BrowserFields, field: IndexField) => - set([field.category, 'fields', field.name], field, accumulator), - {} - ) - : {}, - // Update the value only if _title has changed - (newArgs, lastArgs) => newArgs[0] === lastArgs[0] -); - -export const getDocValueFields = memoizeOne( - (_title: string, fields: IndexField[]): DocValueFields[] => - fields && fields.length > 0 - ? fields.reduce((accumulator: DocValueFields[], field: IndexField) => { - if (field.type === 'date' && accumulator.length < 100) { - const format: string = - field.format != null && !isEmpty(field.format) ? field.format : 'date_time'; - return [ - ...accumulator, - { - field: field.name, - format, - }, - ]; - } - return accumulator; - }, []) - : [], - // Update the value only if _title has changed - (newArgs, lastArgs) => newArgs[0] === lastArgs[0] -); - -export const indicesExistOrDataTemporarilyUnavailable = ( - indicesExist: boolean | null | undefined -) => indicesExist || isUndefined(indicesExist); - -export const EMPTY_BROWSER_FIELDS = {}; -export const EMPTY_DOCVALUE_FIELD: DocValueFields[] = []; diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx index 38af84e0968f86..673db7af2b5e6d 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx @@ -4,28 +4,73 @@ * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable react/display-name */ + +import React from 'react'; import { act, renderHook } from '@testing-library/react-hooks'; +import { Provider } from 'react-redux'; -import { getSourceDefaults, useSourceManager, UseSourceManager } from '.'; +import { useInitSourcerer } from '.'; +import { mockPatterns, mockSource } from './mocks'; +// import { SourcererScopeName } from '../../store/sourcerer/model'; +import { RouteSpyState } from '../../utils/route/types'; +import { SecurityPageName } from '../../../../common/constants'; +import { createStore, State } from '../../store'; import { - mockSourceSelections, - mockSourceGroup, - mockSourceGroups, - mockPatterns, - mockSource, -} from './mocks'; -import { SecurityPageName } from './constants'; -const mockSourceDefaults = mockSource(SecurityPageName.default); + apolloClientObservable, + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, +} from '../../mock'; +const mockSourceDefaults = mockSource; + +const mockRouteSpy: RouteSpyState = { + pageName: SecurityPageName.overview, + detailName: undefined, + tabName: undefined, + search: '', + pathName: '/', +}; +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); +jest.mock('../../utils/route/use_route_spy', () => ({ + useRouteSpy: () => [mockRouteSpy], +})); jest.mock('../../lib/kibana', () => ({ useKibana: jest.fn().mockReturnValue({ services: { + application: { + capabilities: { + siem: { + crud: true, + }, + }, + }, data: { indexPatterns: { getTitles: jest.fn().mockImplementation(() => Promise.resolve(mockPatterns)), }, + search: { + search: jest.fn().mockImplementation(() => ({ + subscribe: jest.fn().mockImplementation(() => ({ + error: jest.fn(), + next: jest.fn(), + })), + })), + }, }, + notifications: {}, }, }), + useUiSetting$: jest.fn().mockImplementation(() => [mockPatterns]), })); jest.mock('../../utils/apollo_context', () => ({ useApolloClient: jest.fn().mockReturnValue({ @@ -34,148 +79,193 @@ jest.mock('../../utils/apollo_context', () => ({ })); describe('Sourcerer Hooks', () => { - const testId = SecurityPageName.default; - const uninitializedId = SecurityPageName.host; + // const testId = SourcererScopeName.default; + // const uninitializedId = SourcererScopeName.detections; beforeEach(() => { jest.clearAllMocks(); jest.restoreAllMocks(); }); + const state: State = mockGlobalState; + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + kibanaObservable, + storage + ); + + beforeEach(() => { + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + kibanaObservable, + storage + ); + }); describe('Initialization', () => { - it('initializes loading default index patterns', async () => { + it('initializes loading default and timeline index patterns', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ - activeSourceGroupId: 'default', - availableIndexPatterns: [], - availableSourceGroupIds: [], - isIndexPatternsLoading: true, - sourceGroups: {}, - getManageSourceGroupById: result.current.getManageSourceGroupById, - initializeSourceGroup: result.current.initializeSourceGroup, - setActiveSourceGroupId: result.current.setActiveSourceGroupId, - updateSourceGroupIndicies: result.current.updateSourceGroupIndicies, + const { waitForNextUpdate } = renderHook(() => useInitSourcerer(), { + wrapper: ({ children }) => {children}, }); - }); - }); - it('initializes loading default source group', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); await waitForNextUpdate(); await waitForNextUpdate(); - expect(result.current).toEqual({ - activeSourceGroupId: 'default', - availableIndexPatterns: mockPatterns, - availableSourceGroupIds: [], - isIndexPatternsLoading: false, - sourceGroups: {}, - getManageSourceGroupById: result.current.getManageSourceGroupById, - initializeSourceGroup: result.current.initializeSourceGroup, - setActiveSourceGroupId: result.current.setActiveSourceGroupId, - updateSourceGroupIndicies: result.current.updateSourceGroupIndicies, + expect(mockDispatch).toBeCalledTimes(2); + expect(mockDispatch.mock.calls[0][0]).toEqual({ + type: 'x-pack/security_solution/local/sourcerer/SET_SOURCERER_SCOPE_LOADING', + payload: { id: 'default', loading: true }, }); - }); - }); - it('initialize completes with formatted source group data', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - activeSourceGroupId: testId, - availableIndexPatterns: mockPatterns, - availableSourceGroupIds: [testId], - isIndexPatternsLoading: false, - sourceGroups: { - default: mockSourceGroup(testId), - }, - getManageSourceGroupById: result.current.getManageSourceGroupById, - initializeSourceGroup: result.current.initializeSourceGroup, - setActiveSourceGroupId: result.current.setActiveSourceGroupId, - updateSourceGroupIndicies: result.current.updateSourceGroupIndicies, + expect(mockDispatch.mock.calls[1][0]).toEqual({ + type: 'x-pack/security_solution/local/sourcerer/SET_SOURCERER_SCOPE_LOADING', + payload: { id: 'timeline', loading: true }, }); + // expect(mockDispatch.mock.calls[1][0]).toEqual({ + // type: 'x-pack/security_solution/local/sourcerer/SET_INDEX_PATTERNS_LIST', + // payload: { allIndexPatterns: mockPatterns, kibanaIndexPatterns: [] }, + // }); }); }); + // TO DO sourcerer @S + // it('initializes loading default source group', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook( + // () => useInitSourcerer(), + // { + // wrapper: ({ children }) => {children}, + // } + // ); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // expect(result.current).toEqual({ + // activeSourcererScopeId: 'default', + // kibanaIndexPatterns: mockPatterns, + // isIndexPatternsLoading: false, + // getSourcererScopeById: result.current.getSourcererScopeById, + // setActiveSourcererScopeId: result.current.setActiveSourcererScopeId, + // updateSourcererScopeIndices: result.current.updateSourcererScopeIndices, + // }); + // }); + // }); + // it('initialize completes with formatted source group data', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook( + // () => useInitSourcerer(), + // { + // wrapper: ({ children }) => {children}, + // } + // ); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // expect(result.current).toEqual({ + // activeSourcererScopeId: testId, + // kibanaIndexPatterns: mockPatterns, + // isIndexPatternsLoading: false, + // getSourcererScopeById: result.current.getSourcererScopeById, + // setActiveSourcererScopeId: result.current.setActiveSourcererScopeId, + // updateSourcererScopeIndices: result.current.updateSourcererScopeIndices, + // }); + // }); + // }); }); - describe('Methods', () => { - it('getManageSourceGroupById: initialized source group returns defaults', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); - const initializedSourceGroup = result.current.getManageSourceGroupById(testId); - expect(initializedSourceGroup).toEqual(mockSourceGroup(testId)); - }); - }); - it('getManageSourceGroupById: uninitialized source group returns defaults', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); - const uninitializedSourceGroup = result.current.getManageSourceGroupById(uninitializedId); - expect(uninitializedSourceGroup).toEqual(getSourceDefaults(uninitializedId, mockPatterns)); - }); - }); - it('initializeSourceGroup: initializes source group', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.initializeSourceGroup( - uninitializedId, - mockSourceGroups[uninitializedId], - true - ); - await waitForNextUpdate(); - const initializedSourceGroup = result.current.getManageSourceGroupById(uninitializedId); - expect(initializedSourceGroup.indexPatterns).toEqual(mockSourceSelections[uninitializedId]); - }); - }); - it('setActiveSourceGroupId: active source group id gets set only if it gets initialized first', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - expect(result.current.activeSourceGroupId).toEqual(testId); - result.current.setActiveSourceGroupId(uninitializedId); - expect(result.current.activeSourceGroupId).toEqual(testId); - result.current.initializeSourceGroup(uninitializedId); - result.current.setActiveSourceGroupId(uninitializedId); - expect(result.current.activeSourceGroupId).toEqual(uninitializedId); - }); - }); - it('updateSourceGroupIndicies: updates source group indicies', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); - let sourceGroup = result.current.getManageSourceGroupById(testId); - expect(sourceGroup.indexPatterns).toEqual(mockSourceSelections[testId]); - result.current.updateSourceGroupIndicies(testId, ['endgame-*', 'filebeat-*']); - await waitForNextUpdate(); - sourceGroup = result.current.getManageSourceGroupById(testId); - expect(sourceGroup.indexPatterns).toEqual(['endgame-*', 'filebeat-*']); - }); - }); - }); + // describe('Methods', () => { + // it('getSourcererScopeById: initialized source group returns defaults', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook( + // () => useInitSourcerer(), + // { + // wrapper: ({ children }) => {children}, + // } + // ); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // const initializedSourcererScope = result.current.getSourcererScopeById(testId); + // expect(initializedSourcererScope).toEqual(mockSourcererScope(testId)); + // }); + // }); + // it('getSourcererScopeById: uninitialized source group returns defaults', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook( + // () => useInitSourcerer(), + // { + // wrapper: ({ children }) => {children}, + // } + // ); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // const uninitializedSourcererScope = result.current.getSourcererScopeById(uninitializedId); + // expect(uninitializedSourcererScope).toEqual( + // getSourceDefaults(uninitializedId, mockPatterns) + // ); + // }); + // }); + // // it('initializeSourcererScope: initializes source group', async () => { + // // await act(async () => { + // // const { result, waitForNextUpdate } = renderHook( + // // () => useSourcerer(), + // // { + // // wrapper: ({ children }) => {children}, + // // } + // // ); + // // await waitForNextUpdate(); + // // await waitForNextUpdate(); + // // await waitForNextUpdate(); + // // result.current.initializeSourcererScope( + // // uninitializedId, + // // mockSourcererScopes[uninitializedId], + // // true + // // ); + // // await waitForNextUpdate(); + // // const initializedSourcererScope = result.current.getSourcererScopeById(uninitializedId); + // // expect(initializedSourcererScope.selectedPatterns).toEqual( + // // mockSourcererScopes[uninitializedId] + // // ); + // // }); + // // }); + // it('setActiveSourcererScopeId: active source group id gets set only if it gets initialized first', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook( + // () => useInitSourcerer(), + // { + // wrapper: ({ children }) => {children}, + // } + // ); + // await waitForNextUpdate(); + // expect(result.current.activeSourcererScopeId).toEqual(testId); + // result.current.setActiveSourcererScopeId(uninitializedId); + // expect(result.current.activeSourcererScopeId).toEqual(testId); + // // result.current.initializeSourcererScope(uninitializedId); + // result.current.setActiveSourcererScopeId(uninitializedId); + // expect(result.current.activeSourcererScopeId).toEqual(uninitializedId); + // }); + // }); + // it('updateSourcererScopeIndices: updates source group indices', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook( + // () => useInitSourcerer(), + // { + // wrapper: ({ children }) => {children}, + // } + // ); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // let sourceGroup = result.current.getSourcererScopeById(testId); + // expect(sourceGroup.selectedPatterns).toEqual(mockSourcererScopes[testId]); + // expect(sourceGroup.scopePatterns).toEqual(mockSourcererScopes[testId]); + // result.current.updateSourcererScopeIndices({ + // id: testId, + // selectedPatterns: ['endgame-*', 'filebeat-*'], + // }); + // await waitForNextUpdate(); + // sourceGroup = result.current.getSourcererScopeById(testId); + // expect(sourceGroup.scopePatterns).toEqual(mockSourcererScopes[testId]); + // expect(sourceGroup.selectedPatterns).toEqual(['endgame-*', 'filebeat-*']); + // }); + // }); + // }); }); diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx index 91907b45aa449b..afacd68d715920 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx @@ -4,412 +4,72 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get, noop, isEmpty } from 'lodash/fp'; -import React, { createContext, useCallback, useContext, useEffect, useReducer } from 'react'; -import { IIndexPattern } from 'src/plugins/data/public'; - -import { NO_ALERT_INDEX } from '../../../../common/constants'; -import { useKibana } from '../../lib/kibana'; - -import { SourceQuery } from '../../../graphql/types'; - -import { sourceQuery } from '../source/index.gql_query'; -import { useApolloClient } from '../../utils/apollo_context'; -import { - sourceGroups, - SecurityPageName, - SourceGroupsType, - SOURCERER_FEATURE_FLAG_ON, -} from './constants'; -import { - BrowserFields, - DocValueFields, - EMPTY_BROWSER_FIELDS, - EMPTY_DOCVALUE_FIELD, - getBrowserFields, - getDocValueFields, - getIndexFields, - indicesExistOrDataTemporarilyUnavailable, -} from './format'; - -// TYPES -interface ManageSource { - browserFields: BrowserFields; - defaultPatterns: string[]; - docValueFields: DocValueFields[]; - errorMessage: string | null; - id: SourceGroupsType; - indexPattern: IIndexPattern; - indexPatterns: string[]; - indicesExist: boolean | undefined | null; - loading: boolean; -} - -interface ManageSourceInit extends Partial { - id: SourceGroupsType; -} - -type ManageSourceGroupById = { - [id in SourceGroupsType]?: ManageSource; -}; - -type ActionManageSource = - | { - type: 'SET_SOURCE'; - id: SourceGroupsType; - defaultIndex: string[]; - payload: ManageSourceInit; - } - | { - type: 'SET_IS_SOURCE_LOADING'; - id: SourceGroupsType; - payload: boolean; - } - | { - type: 'SET_ACTIVE_SOURCE_GROUP_ID'; - payload: SourceGroupsType; - } - | { - type: 'SET_AVAILABLE_INDEX_PATTERNS'; - payload: string[]; - } - | { - type: 'SET_IS_INDEX_PATTERNS_LOADING'; - payload: boolean; - }; - -interface ManageSourcerer { - activeSourceGroupId: SourceGroupsType; - availableIndexPatterns: string[]; - availableSourceGroupIds: SourceGroupsType[]; - isIndexPatternsLoading: boolean; - sourceGroups: ManageSourceGroupById; -} - -export interface UseSourceManager extends ManageSourcerer { - getManageSourceGroupById: (id: SourceGroupsType) => ManageSource; - initializeSourceGroup: ( - id: SourceGroupsType, - indexToAdd?: string[] | null, - onlyCheckIndexToAdd?: boolean - ) => void; - setActiveSourceGroupId: (id: SourceGroupsType) => void; - updateSourceGroupIndicies: (id: SourceGroupsType, updatedIndicies: string[]) => void; -} - -// DEFAULTS/INIT -export const getSourceDefaults = (id: SourceGroupsType, defaultIndex: string[]) => ({ - browserFields: EMPTY_BROWSER_FIELDS, - defaultPatterns: defaultIndex, - docValueFields: EMPTY_DOCVALUE_FIELD, - errorMessage: null, - id, - indexPattern: getIndexFields(defaultIndex.join(), []), - indexPatterns: defaultIndex, - indicesExist: indicesExistOrDataTemporarilyUnavailable(undefined), - loading: true, -}); - -const initManageSource: ManageSourcerer = { - activeSourceGroupId: SecurityPageName.default, - availableIndexPatterns: [], - availableSourceGroupIds: [], - isIndexPatternsLoading: true, - sourceGroups: {}, -}; -const init: UseSourceManager = { - ...initManageSource, - getManageSourceGroupById: (id: SourceGroupsType) => getSourceDefaults(id, []), - initializeSourceGroup: () => noop, - setActiveSourceGroupId: () => noop, - updateSourceGroupIndicies: () => noop, -}; - -const reducerManageSource = (state: ManageSourcerer, action: ActionManageSource) => { - switch (action.type) { - case 'SET_SOURCE': - return { - ...state, - sourceGroups: { - ...state.sourceGroups, - [action.id]: { - ...getSourceDefaults(action.id, action.defaultIndex), - ...state.sourceGroups[action.id], - ...action.payload, - }, - }, - availableSourceGroupIds: state.availableSourceGroupIds.includes(action.id) - ? state.availableSourceGroupIds - : [...state.availableSourceGroupIds, action.id], - }; - case 'SET_IS_SOURCE_LOADING': - return { - ...state, - sourceGroups: { - ...state.sourceGroups, - [action.id]: { - ...state.sourceGroups[action.id], - id: action.id, - loading: action.payload, - }, - }, - }; - case 'SET_ACTIVE_SOURCE_GROUP_ID': - return { - ...state, - activeSourceGroupId: action.payload, - }; - case 'SET_AVAILABLE_INDEX_PATTERNS': - return { - ...state, - availableIndexPatterns: action.payload, - }; - case 'SET_IS_INDEX_PATTERNS_LOADING': - return { - ...state, - isIndexPatternsLoading: action.payload, - }; - default: - return state; - } -}; - -// HOOKS -export const useSourceManager = (): UseSourceManager => { - const { - services: { - data: { indexPatterns }, - }, - } = useKibana(); - const apolloClient = useApolloClient(); - const [state, dispatch] = useReducer(reducerManageSource, initManageSource); - - // Kibana Index Patterns - const setIsIndexPatternsLoading = useCallback((loading: boolean) => { - dispatch({ - type: 'SET_IS_INDEX_PATTERNS_LOADING', - payload: loading, - }); - }, []); - const getDefaultIndex = useCallback( - (indexToAdd?: string[] | null, onlyCheckIndexToAdd?: boolean) => { - const filterIndexAdd = (indexToAdd ?? []).filter((item) => item !== NO_ALERT_INDEX); - if (!isEmpty(filterIndexAdd)) { - return onlyCheckIndexToAdd - ? filterIndexAdd.sort() - : [ - ...state.availableIndexPatterns, - ...filterIndexAdd.filter((index) => !state.availableIndexPatterns.includes(index)), - ].sort(); - } - return state.availableIndexPatterns.sort(); - }, - [state.availableIndexPatterns] - ); - const setAvailableIndexPatterns = useCallback((availableIndexPatterns: string[]) => { - dispatch({ - type: 'SET_AVAILABLE_INDEX_PATTERNS', - payload: availableIndexPatterns, - }); - }, []); - const fetchKibanaIndexPatterns = useCallback(() => { - setIsIndexPatternsLoading(true); - const abortCtrl = new AbortController(); - - async function fetchTitles() { - try { - const result = await indexPatterns.getTitles(); - setAvailableIndexPatterns(result); - setIsIndexPatternsLoading(false); - } catch (error) { - setIsIndexPatternsLoading(false); - } - } - - fetchTitles(); - - return () => { - return abortCtrl.abort(); - }; - }, [indexPatterns, setAvailableIndexPatterns, setIsIndexPatternsLoading]); - - // Security Solution Source Groups - const setActiveSourceGroupId = useCallback( - (sourceGroupId: SourceGroupsType) => { - if (state.availableSourceGroupIds.includes(sourceGroupId)) { - dispatch({ - type: 'SET_ACTIVE_SOURCE_GROUP_ID', - payload: sourceGroupId, - }); - } - }, - [state.availableSourceGroupIds] - ); - const setIsSourceLoading = useCallback( - ({ id, loading }: { id: SourceGroupsType; loading: boolean }) => { - dispatch({ - type: 'SET_IS_SOURCE_LOADING', - id, - payload: loading, - }); - }, +import deepEqual from 'fast-deep-equal'; +import isEqual from 'lodash/isEqual'; +import { useEffect, useMemo } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; + +import { sourcererActions, sourcererSelectors } from '../../store/sourcerer'; +import { ManageScope, SourcererScopeName } from '../../store/sourcerer/model'; +import { useIndexFields } from '../source'; +import { State } from '../../store'; +import { useUserInfo } from '../../../detections/components/user_info'; + +export const useInitSourcerer = ( + scopeId: SourcererScopeName.default | SourcererScopeName.detections = SourcererScopeName.default +) => { + const dispatch = useDispatch(); + + const { loading: loadingSignalIndex, isSignalIndexExists, signalIndexName } = useUserInfo(); + const getConfigIndexPatternsSelector = useMemo( + () => sourcererSelectors.configIndexPatternsSelector(), [] ); - const enrichSource = useCallback( - (id: SourceGroupsType, indexToAdd?: string[] | null, onlyCheckIndexToAdd?: boolean) => { - let isSubscribed = true; - const abortCtrl = new AbortController(); - const defaultIndex = getDefaultIndex(indexToAdd, onlyCheckIndexToAdd); - const selectedPatterns = defaultIndex.filter((pattern) => - state.availableIndexPatterns.includes(pattern) - ); - if (state.sourceGroups[id] == null) { - dispatch({ - type: 'SET_SOURCE', - id, - defaultIndex: selectedPatterns, - payload: { defaultPatterns: defaultIndex, id }, - }); - } - - async function fetchSource() { - if (!apolloClient) return; - setIsSourceLoading({ id, loading: true }); - try { - const result = await apolloClient.query({ - query: sourceQuery, - fetchPolicy: 'network-only', - variables: { - sourceId: 'default', // always - defaultIndex: selectedPatterns, - }, - context: { - fetchOptions: { - signal: abortCtrl.signal, - }, - }, - }); - if (isSubscribed) { - dispatch({ - type: 'SET_SOURCE', - id, - defaultIndex: selectedPatterns, - payload: { - browserFields: getBrowserFields( - selectedPatterns.join(), - get('data.source.status.indexFields', result) - ), - docValueFields: getDocValueFields( - selectedPatterns.join(), - get('data.source.status.indexFields', result) - ), - errorMessage: null, - id, - indexPattern: getIndexFields( - selectedPatterns.join(), - get('data.source.status.indexFields', result) - ), - indexPatterns: selectedPatterns, - indicesExist: indicesExistOrDataTemporarilyUnavailable( - get('data.source.status.indicesExist', result) - ), - loading: false, - }, - }); - } - } catch (error) { - if (isSubscribed) { - dispatch({ - type: 'SET_SOURCE', - id, - defaultIndex: selectedPatterns, - payload: { - errorMessage: error.message, - id, - loading: false, - }, - }); - } - } - } - - fetchSource(); - - return () => { - isSubscribed = false; - return abortCtrl.abort(); - }; - }, - [ - apolloClient, - getDefaultIndex, - setIsSourceLoading, - state.availableIndexPatterns, - state.sourceGroups, - ] - ); + const ConfigIndexPatterns = useSelector(getConfigIndexPatternsSelector, isEqual); - const initializeSourceGroup = useCallback( - (id: SourceGroupsType, indexToAdd?: string[] | null, onlyCheckIndexToAdd?: boolean) => - enrichSource(id, indexToAdd, onlyCheckIndexToAdd), - [enrichSource] - ); - - const updateSourceGroupIndicies = useCallback( - (id: SourceGroupsType, updatedIndicies: string[]) => enrichSource(id, updatedIndicies, true), - [enrichSource] - ); - const getManageSourceGroupById = useCallback( - (id: SourceGroupsType) => { - const sourceById = state.sourceGroups[id]; - if (sourceById != null) { - return sourceById; - } - return getSourceDefaults(id, getDefaultIndex()); - }, - [getDefaultIndex, state.sourceGroups] - ); + useIndexFields(scopeId); + useIndexFields(SourcererScopeName.timeline); - // load initial default index useEffect(() => { - fetchKibanaIndexPatterns(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + if (!loadingSignalIndex && signalIndexName != null) { + dispatch(sourcererActions.setSignalIndexName({ signalIndexName })); + } + }, [dispatch, loadingSignalIndex, signalIndexName]); + // Related to timeline useEffect(() => { - if (!state.isIndexPatternsLoading) { - Object.entries(sourceGroups).forEach(([key, value]) => - initializeSourceGroup(key as SourceGroupsType, value, true) + if (!loadingSignalIndex && signalIndexName != null) { + dispatch( + sourcererActions.setSelectedIndexPatterns({ + id: SourcererScopeName.timeline, + selectedPatterns: [...ConfigIndexPatterns, signalIndexName], + }) ); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state.isIndexPatternsLoading]); + }, [ConfigIndexPatterns, dispatch, loadingSignalIndex, signalIndexName]); - return { - ...state, - getManageSourceGroupById, - initializeSourceGroup, - setActiveSourceGroupId, - updateSourceGroupIndicies, - }; + // Related to the detection page + useEffect(() => { + if ( + scopeId === SourcererScopeName.detections && + isSignalIndexExists && + signalIndexName != null + ) { + dispatch( + sourcererActions.setSelectedIndexPatterns({ + id: scopeId, + selectedPatterns: [signalIndexName], + }) + ); + } + }, [dispatch, isSignalIndexExists, scopeId, signalIndexName]); }; -const ManageSourceContext = createContext(init); - -export const useManageSource = () => useContext(ManageSourceContext); - -interface ManageSourceProps { - children: React.ReactNode; -} - -export const MaybeManageSource = ({ children }: ManageSourceProps) => { - const indexPatternManager = useSourceManager(); - return ( - - {children} - +export const useSourcererScope = (scope: SourcererScopeName = SourcererScopeName.default) => { + const sourcererScopeSelector = useMemo(() => sourcererSelectors.getSourcererScopeSelector(), []); + const SourcererScope = useSelector( + (state) => sourcererScopeSelector(state, scope), + deepEqual ); + return SourcererScope; }; -export const ManageSource = SOURCERER_FEATURE_FLAG_ON - ? MaybeManageSource - : ({ children }: ManageSourceProps) => <>{children}; diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/mocks.ts b/x-pack/plugins/security_solution/public/common/containers/sourcerer/mocks.ts index cde14e54694f08..c34a6917f300ec 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/mocks.ts +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/mocks.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SecurityPageName } from './constants'; -import { getSourceDefaults } from './index'; +import { initSourcererScope } from '../../store/sourcerer/model'; export const mockPatterns = [ 'auditbeat-*', @@ -14,32 +13,10 @@ export const mockPatterns = [ 'logs-*', 'packetbeat-*', 'winlogbeat-*', + 'journalbeat-*', ]; -export const mockSourceGroups = { - [SecurityPageName.default]: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'blobbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'winlogbeat-*', - ], - [SecurityPageName.host]: [ - 'apm-*-transaction*', - 'endgame-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ], -}; - -export const mockSourceSelections = { - [SecurityPageName.default]: ['auditbeat-*', 'endgame-*', 'filebeat-*', 'logs-*', 'winlogbeat-*'], - [SecurityPageName.host]: ['endgame-*', 'logs-*', 'packetbeat-*', 'winlogbeat-*'], -}; -export const mockSource = (testId: SecurityPageName.default | SecurityPageName.host) => ({ +export const mockSource = { data: { source: { id: 'default', @@ -50,7 +27,7 @@ export const mockSource = (testId: SecurityPageName.default | SecurityPageName.h category: '_id', description: 'Each document has an _id that uniquely identifies it', example: 'Y-6TfmcB0WOhS6qyMv3s', - indexes: mockSourceSelections[testId], + indexes: mockPatterns, name: '_id', searchable: true, type: 'string', @@ -67,48 +44,45 @@ export const mockSource = (testId: SecurityPageName.default | SecurityPageName.h loading: false, networkStatus: 7, stale: false, -}); +}; -export const mockSourceGroup = (testId: SecurityPageName.default | SecurityPageName.host) => { - const indexes = mockSourceSelections[testId]; - return { - ...getSourceDefaults(testId, mockPatterns), - defaultPatterns: mockSourceGroups[testId], - browserFields: { - _id: { - fields: { - _id: { - __typename: 'IndexField', - aggregatable: false, - category: '_id', - description: 'Each document has an _id that uniquely identifies it', - esTypes: null, - example: 'Y-6TfmcB0WOhS6qyMv3s', - format: null, - indexes, - name: '_id', - searchable: true, - subType: null, - type: 'string', - }, - }, - }, - }, - indexPattern: { - fields: [ - { +export const mockSourcererScope = { + ...initSourcererScope, + scopePatterns: mockPatterns, + browserFields: { + _id: { + fields: { + _id: { + __typename: 'IndexField', aggregatable: false, + category: '_id', + description: 'Each document has an _id that uniquely identifies it', esTypes: null, + example: 'Y-6TfmcB0WOhS6qyMv3s', + format: null, + indexes: mockPatterns, name: '_id', searchable: true, subType: null, type: 'string', }, - ], - title: indexes.join(), + }, }, - indexPatterns: indexes, - indicesExist: true, - loading: false, - }; + }, + indexPattern: { + fields: [ + { + aggregatable: false, + esTypes: null, + name: '_id', + searchable: true, + subType: null, + type: 'string', + }, + ], + title: mockPatterns.join(), + }, + selectedPatterns: mockPatterns, + indicesExist: true, + loading: false, }; diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts index 573ef92f7e0697..3051459d5de0ca 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts @@ -12,9 +12,25 @@ import { createStartServicesMock, createWithKibanaMock, } from '../kibana_react.mock'; - +const mockStartServicesMock = createStartServicesMock(); export const KibanaServices = { get: jest.fn(), getKibanaVersion: jest.fn(() => '8.0.0') }; -export const useKibana = jest.fn().mockReturnValue({ services: createStartServicesMock() }); +export const useKibana = jest.fn().mockReturnValue({ + services: { + ...mockStartServicesMock, + data: { + ...mockStartServicesMock.data, + search: { + ...mockStartServicesMock.data.search, + search: jest.fn().mockImplementation(() => ({ + subscribe: jest.fn().mockImplementation(() => ({ + error: jest.fn(), + next: jest.fn(), + })), + })), + }, + }, + }, +}); export const useUiSetting = jest.fn(createUseUiSettingMock()); export const useUiSetting$ = jest.fn(createUseUiSetting$Mock()); export const useHttp = jest.fn().mockReturnValue(createStartServicesMock().http); diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts index a74c9a6d2009da..0944b6aa27f678 100644 --- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts +++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts @@ -22,11 +22,15 @@ import { DEFAULT_TO, DEFAULT_INTERVAL_TYPE, DEFAULT_INTERVAL_VALUE, + DEFAULT_INDEX_PATTERN, } from '../../../common/constants'; import { networkModel } from '../../network/store'; import { TimelineType, TimelineStatus } from '../../../common/types/timeline'; import { mockManagementState } from '../../management/store/reducer'; import { ManagementState } from '../../management/types'; +import { initialSourcererState, SourcererScopeName } from '../store/sourcerer/model'; +import { mockBrowserFields, mockDocValueFields } from '../containers/source/mock'; +import { mockIndexPattern } from './index_pattern'; export const mockGlobalState: State = { app: { @@ -203,6 +207,7 @@ export const mockGlobalState: State = { id: 'test', savedObjectId: null, columns: defaultHeaders, + indexNames: DEFAULT_INDEX_PATTERN, itemsPerPage: 5, dataProviders: [], description: '', @@ -241,6 +246,28 @@ export const mockGlobalState: State = { }, insertTimeline: null, }, + sourcerer: { + ...initialSourcererState, + sourcererScopes: { + ...initialSourcererState.sourcererScopes, + [SourcererScopeName.default]: { + ...initialSourcererState.sourcererScopes[SourcererScopeName.default], + selectedPatterns: DEFAULT_INDEX_PATTERN, + browserFields: mockBrowserFields, + indexPattern: mockIndexPattern, + docValueFields: mockDocValueFields, + loading: false, + }, + [SourcererScopeName.timeline]: { + ...initialSourcererState.sourcererScopes[SourcererScopeName.timeline], + selectedPatterns: DEFAULT_INDEX_PATTERN, + browserFields: mockBrowserFields, + indexPattern: mockIndexPattern, + docValueFields: mockDocValueFields, + loading: false, + }, + }, + }, /** * These state's are wrapped in `Immutable`, but for compatibility with the overall app architecture, * they are cast to mutable versions here. diff --git a/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts b/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts index 826057560f942c..e4abc17e9034ce 100644 --- a/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts +++ b/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export const mockIndexPattern = { +import { IIndexPattern } from '../../../../../../src/plugins/data/common/index_patterns'; + +export const mockIndexPattern: IIndexPattern = { fields: [ { name: '@timestamp', @@ -93,3 +95,5 @@ export const mockIndexPattern = { ], title: 'filebeat-*,auditbeat-*,packetbeat-*', }; + +export const mockIndexNames = ['filebeat-*', 'auditbeat-*', 'packetbeat-*']; diff --git a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts index 26013915315afb..6403a50ad4a1dc 100644 --- a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts +++ b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts @@ -2124,6 +2124,7 @@ export const mockTimelineModel: TimelineModel = { highlightedDropAndProviderId: '', historyIds: [], id: 'ef579e40-jibber-jabber', + indexNames: [], isFavorite: false, isLive: false, isLoading: false, @@ -2228,6 +2229,7 @@ export const defaultTimelineProps: CreateTimelineProps = { highlightedDropAndProviderId: '', historyIds: [], id: TimelineId.active, + indexNames: [], isFavorite: false, isLive: false, isLoading: false, diff --git a/x-pack/plugins/security_solution/public/common/store/actions.ts b/x-pack/plugins/security_solution/public/common/store/actions.ts index 6b446ab6692d92..f4134b5c47c2cb 100644 --- a/x-pack/plugins/security_solution/public/common/store/actions.ts +++ b/x-pack/plugins/security_solution/public/common/store/actions.ts @@ -12,6 +12,7 @@ import { TrustedAppsPageAction } from '../../management/pages/trusted_apps/store export { appActions } from './app'; export { dragAndDropActions } from './drag_and_drop'; export { inputsActions } from './inputs'; +export { sourcererActions } from './sourcerer'; import { RoutingAction } from './routing'; export type AppAction = diff --git a/x-pack/plugins/security_solution/public/common/store/model.ts b/x-pack/plugins/security_solution/public/common/store/model.ts index 0032a95cce321a..04603d06075834 100644 --- a/x-pack/plugins/security_solution/public/common/store/model.ts +++ b/x-pack/plugins/security_solution/public/common/store/model.ts @@ -7,4 +7,5 @@ export { appModel } from './app'; export { dragAndDropModel } from './drag_and_drop'; export { inputsModel } from './inputs'; +export { sourcererModel } from './sourcerer'; export * from './types'; diff --git a/x-pack/plugins/security_solution/public/common/store/reducer.ts b/x-pack/plugins/security_solution/public/common/store/reducer.ts index a0977cea71da7a..60cb6a4e960bd4 100644 --- a/x-pack/plugins/security_solution/public/common/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/common/store/reducer.ts @@ -9,6 +9,7 @@ import { combineReducers, PreloadedState, AnyAction, Reducer } from 'redux'; import { appReducer, initialAppState } from './app'; import { dragAndDropReducer, initialDragAndDropState } from './drag_and_drop'; import { createInitialInputsState, inputsReducer } from './inputs'; +import { sourcererReducer, sourcererModel } from './sourcerer'; import { HostsPluginReducer } from '../../hosts/store'; import { NetworkPluginReducer } from '../../network/store'; @@ -18,6 +19,7 @@ import { SecuritySubPlugins } from '../../app/types'; import { ManagementPluginReducer } from '../../management'; import { State } from './types'; import { AppAction } from './actions'; +import { KibanaIndexPatterns } from './sourcerer/model'; export type SubPluginsInitReducer = HostsPluginReducer & NetworkPluginReducer & @@ -28,13 +30,22 @@ export type SubPluginsInitReducer = HostsPluginReducer & * Factory for the 'initialState' that is used to preload state into the Security App's redux store. */ export const createInitialState = ( - pluginsInitState: SecuritySubPlugins['store']['initialState'] + pluginsInitState: SecuritySubPlugins['store']['initialState'], + { + kibanaIndexPatterns, + configIndexPatterns, + }: { kibanaIndexPatterns: KibanaIndexPatterns; configIndexPatterns: string[] } ): PreloadedState => { const preloadedState: PreloadedState = { app: initialAppState, dragAndDrop: initialDragAndDropState, ...pluginsInitState, inputs: createInitialInputsState(), + sourcerer: { + ...sourcererModel.initialSourcererState, + kibanaIndexPatterns, + configIndexPatterns, + }, }; return preloadedState; }; @@ -49,5 +60,6 @@ export const createReducer: ( app: appReducer, dragAndDrop: dragAndDropReducer, inputs: inputsReducer, + sourcerer: sourcererReducer, ...pluginsReducer, }); diff --git a/x-pack/plugins/security_solution/public/common/store/selectors.ts b/x-pack/plugins/security_solution/public/common/store/selectors.ts index b938bae39b634b..3cefd92bf9e602 100644 --- a/x-pack/plugins/security_solution/public/common/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/common/store/selectors.ts @@ -7,3 +7,4 @@ export { appSelectors } from './app'; export { dragAndDropSelectors } from './drag_and_drop'; export { inputsSelectors } from './inputs'; +export { sourcererSelectors } from './sourcerer'; diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/actions.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/actions.ts new file mode 100644 index 00000000000000..0b40586798f09e --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/actions.ts @@ -0,0 +1,36 @@ +/* + * 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 actionCreatorFactory from 'typescript-fsa'; +import { TimelineEventsType } from '../../../../common/types/timeline'; + +import { KibanaIndexPatterns, ManageScopeInit, SourcererScopeName } from './model'; + +const actionCreator = actionCreatorFactory('x-pack/security_solution/local/sourcerer'); + +export const setSource = actionCreator<{ + id: SourcererScopeName; + payload: ManageScopeInit; +}>('SET_SOURCE'); + +export const setIndexPatternsList = actionCreator<{ + kibanaIndexPatterns: KibanaIndexPatterns; + configIndexPatterns: string[]; +}>('SET_INDEX_PATTERNS_LIST'); + +export const setSignalIndexName = actionCreator<{ signalIndexName: string }>( + 'SET_SIGNAL_INDEX_NAME' +); + +export const setSourcererScopeLoading = actionCreator<{ id: SourcererScopeName; loading: boolean }>( + 'SET_SOURCERER_SCOPE_LOADING' +); + +export const setSelectedIndexPatterns = actionCreator<{ + id: SourcererScopeName; + selectedPatterns: string[]; + eventType?: TimelineEventsType; +}>('SET_SELECTED_INDEX_PATTERNS'); diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/index.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/index.ts new file mode 100644 index 00000000000000..551c7d8e3efbc6 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/index.ts @@ -0,0 +1,12 @@ +/* + * 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 * as sourcererActions from './actions'; +import * as sourcererModel from './model'; +import * as sourcererSelectors from './selectors'; + +export { sourcererActions, sourcererModel, sourcererSelectors }; +export * from './reducer'; diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts new file mode 100644 index 00000000000000..93f7ff95dfb00e --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts @@ -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 { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; +import { DocValueFields } from '../../../../common/search_strategy/common'; +import { + BrowserFields, + EMPTY_BROWSER_FIELDS, + EMPTY_DOCVALUE_FIELD, + EMPTY_INDEX_PATTERN, +} from '../../../../common/search_strategy/index_fields'; + +export type ErrorModel = Error[]; + +export enum SourcererScopeName { + default = 'default', + detections = 'detections', + timeline = 'timeline', +} + +export interface ManageScope { + browserFields: BrowserFields; + docValueFields: DocValueFields[]; + errorMessage: string | null; + id: SourcererScopeName; + indexPattern: IIndexPattern; + indicesExist: boolean | undefined | null; + loading: boolean; + selectedPatterns: string[]; +} + +export interface ManageScopeInit extends Partial { + id: SourcererScopeName; +} + +export type SourcererScopeById = { + [id in SourcererScopeName]: ManageScope; +}; + +export type KibanaIndexPatterns = Array<{ id: string; title: string }>; + +// ManageSourcerer +export interface SourcererModel { + kibanaIndexPatterns: KibanaIndexPatterns; + configIndexPatterns: string[]; + signalIndexName: string | null; + sourcererScopes: SourcererScopeById; +} + +export const initSourcererScope = { + browserFields: EMPTY_BROWSER_FIELDS, + docValueFields: EMPTY_DOCVALUE_FIELD, + errorMessage: null, + indexPattern: EMPTY_INDEX_PATTERN, + indicesExist: true, + loading: true, + selectedPatterns: [], +}; + +export const initialSourcererState: SourcererModel = { + kibanaIndexPatterns: [], + configIndexPatterns: [], + signalIndexName: null, + sourcererScopes: { + [SourcererScopeName.default]: { + ...initSourcererScope, + id: SourcererScopeName.default, + }, + [SourcererScopeName.detections]: { + ...initSourcererScope, + id: SourcererScopeName.detections, + }, + [SourcererScopeName.timeline]: { + ...initSourcererScope, + id: SourcererScopeName.timeline, + }, + }, +}; + +export type FSourcererScopePatterns = { + [id in SourcererScopeName]: string[]; +}; +export type SourcererScopePatterns = Partial; diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/reducer.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/reducer.ts new file mode 100644 index 00000000000000..b65d4d6338e504 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/reducer.ts @@ -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 isEmpty from 'lodash/isEmpty'; +import { reducerWithInitialState } from 'typescript-fsa-reducers'; + +import { + setIndexPatternsList, + setSourcererScopeLoading, + setSelectedIndexPatterns, + setSignalIndexName, + setSource, +} from './actions'; +import { initialSourcererState, SourcererModel, SourcererScopeName } from './model'; + +export type SourcererState = SourcererModel; + +export const sourcererReducer = reducerWithInitialState(initialSourcererState) + .case(setIndexPatternsList, (state, { kibanaIndexPatterns, configIndexPatterns }) => ({ + ...state, + kibanaIndexPatterns, + configIndexPatterns, + })) + .case(setSignalIndexName, (state, { signalIndexName }) => ({ + ...state, + signalIndexName, + })) + .case(setSourcererScopeLoading, (state, { id, loading }) => ({ + ...state, + sourcererScopes: { + ...state.sourcererScopes, + [id]: { + ...state.sourcererScopes[id], + loading, + }, + }, + })) + .case(setSelectedIndexPatterns, (state, { id, selectedPatterns, eventType }) => { + const kibanaIndexPatterns = state.kibanaIndexPatterns.map((kip) => kip.title); + const newSelectedPatterns = selectedPatterns.filter( + (sp) => + state.configIndexPatterns.includes(sp) || + kibanaIndexPatterns.includes(sp) || + (!isEmpty(state.signalIndexName) && state.signalIndexName === sp) + ); + let defaultIndexPatterns = state.configIndexPatterns; + if (id === SourcererScopeName.timeline && isEmpty(newSelectedPatterns)) { + if (eventType === 'all' && !isEmpty(state.signalIndexName)) { + defaultIndexPatterns = [...state.configIndexPatterns, state.signalIndexName ?? '']; + } else if (eventType === 'raw') { + defaultIndexPatterns = state.configIndexPatterns; + } else if ( + !isEmpty(state.signalIndexName) && + (eventType === 'signal' || eventType === 'alert') + ) { + defaultIndexPatterns = [state.signalIndexName ?? '']; + } + } else if (id === SourcererScopeName.detections && isEmpty(newSelectedPatterns)) { + defaultIndexPatterns = [state.signalIndexName ?? '']; + } + return { + ...state, + sourcererScopes: { + ...state.sourcererScopes, + [id]: { + ...state.sourcererScopes[id], + selectedPatterns: isEmpty(newSelectedPatterns) + ? defaultIndexPatterns + : newSelectedPatterns, + }, + }, + }; + }) + .case(setSource, (state, { id, payload }) => { + const { ...sourcererScopes } = payload; + return { + ...state, + sourcererScopes: { + ...state.sourcererScopes, + [id]: { + ...state.sourcererScopes[id], + ...sourcererScopes, + ...(state.sourcererScopes[id].selectedPatterns.length === 0 + ? { selectedPatterns: state.configIndexPatterns } + : {}), + }, + }, + }; + }) + .build(); diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/selectors.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/selectors.ts new file mode 100644 index 00000000000000..ca9ea26ba5bac0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/selectors.ts @@ -0,0 +1,91 @@ +/* + * 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 { createSelector } from 'reselect'; +import { State } from '../types'; +import { SourcererScopeById, KibanaIndexPatterns, SourcererScopeName, ManageScope } from './model'; + +export const sourcererKibanaIndexPatternsSelector = ({ sourcerer }: State): KibanaIndexPatterns => + sourcerer.kibanaIndexPatterns; + +export const sourcererSignalIndexNameSelector = ({ sourcerer }: State): string | null => + sourcerer.signalIndexName; + +export const sourcererConfigIndexPatternsSelector = ({ sourcerer }: State): string[] => + sourcerer.configIndexPatterns; + +export const sourcererScopesSelector = ({ sourcerer }: State): SourcererScopeById => + sourcerer.sourcererScopes; + +export const scopesSelector = () => createSelector(sourcererScopesSelector, (scopes) => scopes); + +export const kibanaIndexPatternsSelector = () => + createSelector( + sourcererKibanaIndexPatternsSelector, + (kibanaIndexPatterns) => kibanaIndexPatterns + ); + +export const signalIndexNameSelector = () => + createSelector(sourcererSignalIndexNameSelector, (signalIndexName) => signalIndexName); + +export const configIndexPatternsSelector = () => + createSelector( + sourcererConfigIndexPatternsSelector, + (configIndexPatterns) => configIndexPatterns + ); + +export const getIndexNamesSelectedSelector = () => { + const getScopesSelector = scopesSelector(); + const getConfigIndexPatternsSelector = configIndexPatternsSelector(); + + const mapStateToProps = (state: State, scopeId: SourcererScopeName): string[] => { + const scope = getScopesSelector(state)[scopeId]; + const configIndexPatterns = getConfigIndexPatternsSelector(state); + + return scope.selectedPatterns.length === 0 ? configIndexPatterns : scope.selectedPatterns; + }; + + return mapStateToProps; +}; + +export const getAllExistingIndexNamesSelector = () => { + const getSignalIndexNameSelector = signalIndexNameSelector(); + const getConfigIndexPatternsSelector = configIndexPatternsSelector(); + + const mapStateToProps = (state: State): string[] => { + const signalIndexName = getSignalIndexNameSelector(state); + const configIndexPatterns = getConfigIndexPatternsSelector(state); + + return signalIndexName != null + ? [...configIndexPatterns, signalIndexName] + : configIndexPatterns; + }; + + return mapStateToProps; +}; + +export const defaultIndexNamesSelector = () => { + const getScopesSelector = scopesSelector(); + const getConfigIndexPatternsSelector = configIndexPatternsSelector(); + + const mapStateToProps = (state: State, scopeId: SourcererScopeName): string[] => { + const scope = getScopesSelector(state)[scopeId]; + const configIndexPatterns = getConfigIndexPatternsSelector(state); + + return scope.selectedPatterns.length === 0 ? configIndexPatterns : scope.selectedPatterns; + }; + + return mapStateToProps; +}; + +export const getSourcererScopeSelector = () => { + const getScopesSelector = scopesSelector(); + + const mapStateToProps = (state: State, scopeId: SourcererScopeName): ManageScope => + getScopesSelector(state)[scopeId]; + + return mapStateToProps; +}; diff --git a/x-pack/plugins/security_solution/public/common/store/types.ts b/x-pack/plugins/security_solution/public/common/store/types.ts index 91d92e4758c4a0..6903567c752bc2 100644 --- a/x-pack/plugins/security_solution/public/common/store/types.ts +++ b/x-pack/plugins/security_solution/public/common/store/types.ts @@ -12,6 +12,7 @@ import { AppAction } from './actions'; import { Immutable } from '../../../common/endpoint/types'; import { AppState } from './app/reducer'; import { InputsState } from './inputs/reducer'; +import { SourcererState } from './sourcerer/reducer'; import { HostsPluginState } from '../../hosts/store'; import { DragAndDropState } from './drag_and_drop/reducer'; import { TimelinePluginState } from '../../timelines/store/timeline'; @@ -25,6 +26,7 @@ export type StoreState = HostsPluginState & app: AppState; dragAndDrop: DragAndDropState; inputs: InputsState; + sourcerer: SourcererState; }; /** * The redux `State` type for the Security App. diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx index 678aaf06e50e45..e3440f41585130 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx @@ -197,6 +197,7 @@ describe('alert actions', () => { highlightedDropAndProviderId: '', historyIds: [], id: '', + indexNames: [], isFavorite: false, isLive: false, isLoading: false, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx index 640726bb2e7c86..7f98d3b2f71de2 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx @@ -279,6 +279,7 @@ export const sendAlertToTimelineAction = async ({ ...getThresholdAggregationDataProvider(ecsData, nonEcsData), ], id: TimelineId.active, + indexNames: [], dateRange: { start: from, end: to, @@ -329,6 +330,7 @@ export const sendAlertToTimelineAction = async ({ }, ], id: TimelineId.active, + indexNames: [], dateRange: { start: from, end: to, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx index be249576020370..6724d3a83d6175 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx @@ -22,7 +22,6 @@ describe('AlertsTableComponent', () => { hasIndexWrite from={'2020-07-07T08:20:18.966Z'} loading - signalsIndex="index" to={'2020-07-08T08:20:18.966Z'} globalQuery={{ query: 'query', diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index 0416b3d2a459f9..d66d37a020040b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -13,7 +13,6 @@ import { Status } from '../../../../common/detection_engine/schemas/common/schem import { Filter, esQuery } from '../../../../../../../src/plugins/data/public'; import { TimelineIdLiteral } from '../../../../common/types/timeline'; import { useAppToasts } from '../../../common/hooks/use_app_toasts'; -import { useFetchIndexPatterns } from '../../containers/detection_engine/rules/fetch_index_patterns'; import { StatefulEventsViewer } from '../../../common/components/events_viewer'; import { HeaderSection } from '../../../common/components/header_section'; import { combineQueries } from '../../../timelines/components/timeline/helpers'; @@ -45,6 +44,8 @@ import { displaySuccessToast, displayErrorToast, } from '../../../common/components/toasters'; +import { SourcererScopeName } from '../../../common/store/sourcerer/model'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; interface OwnProps { timelineId: TimelineIdLiteral; @@ -55,7 +56,6 @@ interface OwnProps { loading: boolean; showBuildingBlockAlerts: boolean; onShowBuildingBlockAlertsChanged: (showBuildingBlockAlerts: boolean) => void; - signalsIndex: string; to: string; } @@ -80,19 +80,20 @@ export const AlertsTableComponent: React.FC = ({ setEventsLoading, showBuildingBlockAlerts, onShowBuildingBlockAlertsChanged, - signalsIndex, to, }) => { const [showClearSelectionAction, setShowClearSelectionAction] = useState(false); const [filterGroup, setFilterGroup] = useState(FILTER_OPEN); - const [{ browserFields, indexPatterns, isLoading: indexPatternsLoading }] = useFetchIndexPatterns( - signalsIndex !== '' ? [signalsIndex] : [], - 'alerts_table' - ); + const { + browserFields, + indexPattern: indexPatterns, + loading: indexPatternsLoading, + selectedPatterns, + } = useSourcererScope(SourcererScopeName.detections); const kibana = useKibana(); const [, dispatchToaster] = useStateToaster(); const { addWarning } = useAppToasts(); - const { initializeTimeline, setSelectAll, setIndexToAdd } = useManageTimeline(); + const { initializeTimeline, setSelectAll } = useManageTimeline(); const getGlobalQuery = useCallback( (customFilters: Filter[]) => { @@ -284,7 +285,6 @@ export const AlertsTableComponent: React.FC = ({ ] ); - const defaultIndices = useMemo(() => [signalsIndex], [signalsIndex]); const defaultFiltersMemo = useMemo(() => { if (isEmpty(defaultFilters)) { return buildAlertStatusFilter(filterGroup); @@ -301,7 +301,6 @@ export const AlertsTableComponent: React.FC = ({ filterManager, footerText: i18n.TOTAL_COUNT_OF_ALERTS, id: timelineId, - indexToAdd: defaultIndices, loadingText: i18n.LOADING_ALERTS, selectAll: false, queryFields: requiredFieldsForActions, @@ -310,16 +309,12 @@ export const AlertsTableComponent: React.FC = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useEffect(() => { - setIndexToAdd({ id: timelineId, indexToAdd: defaultIndices }); - }, [timelineId, defaultIndices, setIndexToAdd]); - const headerFilterGroup = useMemo( () => , [onFilterGroupChangedCallback] ); - if (loading || indexPatternsLoading || isEmpty(signalsIndex)) { + if (loading || indexPatternsLoading || isEmpty(selectedPatterns)) { return ( @@ -330,12 +325,12 @@ export const AlertsTableComponent: React.FC = ({ return ( diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx index 4559e44b8c3c5c..82fed152ea66d9 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx @@ -109,7 +109,7 @@ const AlertContextMenuComponent: React.FC = ({ const closeAddExceptionModal = useCallback(() => { setShouldShowAddExceptionModal(false); setAddExceptionModalState(addExceptionModalInitialState); - }, [setShouldShowAddExceptionModal, setAddExceptionModalState]); + }, []); const onAddExceptionCancel = useCallback(() => { closeAddExceptionModal(); @@ -305,33 +305,6 @@ const AlertContextMenuComponent: React.FC = ({ [setShouldShowAddExceptionModal, setAddExceptionModalState] ); - const AddExceptionModal = useCallback( - () => - shouldShowAddExceptionModal === true && addExceptionModalState.alertData !== null ? ( - - ) : null, - [ - shouldShowAddExceptionModal, - addExceptionModalState.alertData, - addExceptionModalState.ruleName, - addExceptionModalState.ruleId, - addExceptionModalState.ruleIndices, - addExceptionModalState.exceptionListType, - onAddExceptionCancel, - onAddExceptionConfirm, - alertStatus, - ] - ); - const button = ( = ({ - + {shouldShowAddExceptionModal === true && addExceptionModalState.alertData !== null && ( + + )} ); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.tsx index 4ab5fa5e6012fd..f4649b016f67c7 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.tsx @@ -15,7 +15,6 @@ import { useApolloClient } from '../../../../common/utils/apollo_context'; import { sendAlertToTimelineAction } from '../actions'; import { dispatchUpdateTimeline } from '../../../../timelines/components/open_timeline/helpers'; import { ActionIconItem } from '../../../../timelines/components/timeline/body/actions/action_icon_item'; - import { CreateTimelineProps } from '../types'; import { ACTION_INVESTIGATE_IN_TIMELINE, @@ -49,6 +48,8 @@ const InvestigateInTimelineActionComponent: React.FC = (props) => ( - + ); DetectionEngineHeaderPageComponent.defaultProps = { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx index cb25785eaa5b29..4312be0b469904 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx @@ -8,8 +8,9 @@ import { mount, shallow } from 'enzyme'; import { ThemeProvider } from 'styled-components'; import euiDarkVars from '@elastic/eui/dist/eui_theme_light.json'; +import { stubIndexPattern } from 'src/plugins/data/common/index_patterns/index_pattern.stub'; import { StepAboutRule } from '.'; - +import { useFetchIndex } from '../../../../common/containers/source'; import { mockAboutStepRule } from '../../../pages/detection_engine/rules/all/__mocks__/mock'; import { StepRuleDescription } from '../description_step'; import { stepAboutDefaultValue } from './default_value'; @@ -20,6 +21,7 @@ import { } from '../../../pages/detection_engine/rules/types'; import { fillEmptySeverityMappings } from '../../../pages/detection_engine/rules/helpers'; +jest.mock('../../../../common/containers/source'); const theme = () => ({ eui: euiDarkVars, darkMode: true }); /* eslint-disable no-console */ @@ -44,6 +46,12 @@ describe('StepAboutRuleComponent', () => { beforeEach(() => { formHook = null; + (useFetchIndex as jest.Mock).mockImplementation(() => [ + false, + { + indexPatterns: stubIndexPattern, + }, + ]); }); test('it renders StepRuleDescription if isReadOnlyView is true and "name" property exists', () => { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx index 66f95f5ce15d21..90b70e53a459e0 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx @@ -39,8 +39,8 @@ import { NextStep } from '../next_step'; import { MarkdownEditorForm } from '../../../../common/components/markdown_editor/eui_form'; import { SeverityField } from '../severity_mapping'; import { RiskScoreField } from '../risk_score_mapping'; -import { useFetchIndexPatterns } from '../../../containers/detection_engine/rules'; import { AutocompleteField } from '../autocomplete_field'; +import { useFetchIndex } from '../../../../common/containers/source'; const CommonUseField = getUseField({ component: Field }); @@ -74,10 +74,8 @@ const StepAboutRuleComponent: FC = ({ }) => { const initialState = defaultValues ?? stepAboutDefaultValue; const [severityValue, setSeverityValue] = useState(initialState.severity.value); - const [{ isLoading: indexPatternLoading, indexPatterns }] = useFetchIndexPatterns( - defineRuleData?.index ?? [], - RuleStep.aboutRule - ); + const [indexPatternLoading, { indexPatterns }] = useFetchIndex(defineRuleData?.index ?? []); + const canUseExceptions = defineRuleData?.ruleType && !isMlRule(defineRuleData.ruleType) && diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx index 7846f0c406668c..99999ddbf19767 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx @@ -14,7 +14,6 @@ import { DEFAULT_TIMELINE_TITLE } from '../../../../timelines/components/timelin import { isMlRule } from '../../../../../common/machine_learning/helpers'; import { hasMlAdminPermissions } from '../../../../../common/machine_learning/has_ml_admin_permissions'; import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license'; -import { useFetchIndexPatterns } from '../../../containers/detection_engine/rules'; import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities'; import { useUiSetting$ } from '../../../../common/lib/kibana'; import { @@ -48,6 +47,7 @@ import { schema } from './schema'; import * as i18n from './translations'; import { isEqlRule, isThresholdRule } from '../../../../../common/detection_engine/utils'; import { EqlQueryBar } from '../eql_query_bar'; +import { useFetchIndex } from '../../../../common/containers/source'; const CommonUseField = getUseField({ component: Field }); @@ -125,10 +125,7 @@ const StepDefineRuleComponent: FC = ({ }) as unknown) as [Partial]; const index = formIndex || initialState.index; const ruleType = formRuleType || initialState.ruleType; - const [{ browserFields, indexPatterns, isLoading: indexPatternsLoading }] = useFetchIndexPatterns( - index, - RuleStep.defineRule - ); + const [indexPatternsLoading, { browserFields, indexPatterns }] = useFetchIndex(index); // reset form when rule type changes useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx index e1a29c3575d95c..00e108ffb89b64 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx @@ -169,22 +169,19 @@ export const useUserInfo = (): State => { if (loading !== privilegeLoading || indexNameLoading) { dispatch({ type: 'updateLoading', loading: privilegeLoading || indexNameLoading }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, privilegeLoading, indexNameLoading]); + }, [dispatch, loading, privilegeLoading, indexNameLoading]); useEffect(() => { if (!loading && hasIndexManage !== hasApiIndexManage && hasApiIndexManage != null) { dispatch({ type: 'updateHasIndexManage', hasIndexManage: hasApiIndexManage }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, hasIndexManage, hasApiIndexManage]); + }, [dispatch, loading, hasIndexManage, hasApiIndexManage]); useEffect(() => { if (!loading && hasIndexWrite !== hasApiIndexWrite && hasApiIndexWrite != null) { dispatch({ type: 'updateHasIndexWrite', hasIndexWrite: hasApiIndexWrite }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, hasIndexWrite, hasApiIndexWrite]); + }, [dispatch, loading, hasIndexWrite, hasApiIndexWrite]); useEffect(() => { if ( @@ -194,36 +191,31 @@ export const useUserInfo = (): State => { ) { dispatch({ type: 'updateIsSignalIndexExists', isSignalIndexExists: isApiSignalIndexExists }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, isSignalIndexExists, isApiSignalIndexExists]); + }, [dispatch, loading, isSignalIndexExists, isApiSignalIndexExists]); useEffect(() => { if (!loading && isAuthenticated !== isApiAuthenticated && isApiAuthenticated != null) { dispatch({ type: 'updateIsAuthenticated', isAuthenticated: isApiAuthenticated }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, isAuthenticated, isApiAuthenticated]); + }, [dispatch, loading, isAuthenticated, isApiAuthenticated]); useEffect(() => { if (!loading && hasEncryptionKey !== isApiEncryptionKey && isApiEncryptionKey != null) { dispatch({ type: 'updateHasEncryptionKey', hasEncryptionKey: isApiEncryptionKey }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, hasEncryptionKey, isApiEncryptionKey]); + }, [dispatch, loading, hasEncryptionKey, isApiEncryptionKey]); useEffect(() => { if (!loading && canUserCRUD !== capabilitiesCanUserCRUD && capabilitiesCanUserCRUD != null) { dispatch({ type: 'updateCanUserCRUD', canUserCRUD: capabilitiesCanUserCRUD }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, canUserCRUD, capabilitiesCanUserCRUD]); + }, [dispatch, loading, canUserCRUD, capabilitiesCanUserCRUD]); useEffect(() => { if (!loading && signalIndexName !== apiSignalIndexName && apiSignalIndexName != null) { dispatch({ type: 'updateSignalIndexName', signalIndexName: apiSignalIndexName }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, signalIndexName, apiSignalIndexName]); + }, [dispatch, loading, signalIndexName, apiSignalIndexName]); useEffect(() => { if ( diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.test.tsx deleted file mode 100644 index d36c19a6a35c66..00000000000000 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.test.tsx +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { renderHook, act } from '@testing-library/react-hooks'; - -import { DEFAULT_INDEX_PATTERN } from '../../../../../common/constants'; -import { useApolloClient } from '../../../../common/utils/apollo_context'; -import { mocksSource } from '../../../../common/containers/source/mock'; - -import { useFetchIndexPatterns, Return } from './fetch_index_patterns'; - -const mockUseApolloClient = useApolloClient as jest.Mock; -jest.mock('../../../../common/utils/apollo_context'); - -describe('useFetchIndexPatterns', () => { - beforeEach(() => { - mockUseApolloClient.mockClear(); - }); - test('happy path', async () => { - await act(async () => { - mockUseApolloClient.mockImplementation(() => ({ - query: () => Promise.resolve(mocksSource[0].result), - })); - const { result, waitForNextUpdate } = renderHook(() => - useFetchIndexPatterns(DEFAULT_INDEX_PATTERN) - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual([ - { - browserFields: { - base: { - fields: { - '@timestamp': { - category: 'base', - description: - 'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.', - example: '2016-05-23T08:05:34.853Z', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: '@timestamp', - searchable: true, - type: 'date', - aggregatable: true, - }, - }, - }, - agent: { - fields: { - 'agent.ephemeral_id': { - category: 'agent', - description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.ephemeral_id', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'agent.hostname': { - category: 'agent', - description: null, - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.hostname', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'agent.id': { - category: 'agent', - description: - 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', - example: '8a4f500d', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.id', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'agent.name': { - category: 'agent', - description: - 'Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', - example: 'foo', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.name', - searchable: true, - type: 'string', - aggregatable: true, - }, - }, - }, - auditd: { - fields: { - 'auditd.data.a0': { - category: 'auditd', - description: null, - example: null, - format: '', - indexes: ['auditbeat'], - name: 'auditd.data.a0', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'auditd.data.a1': { - category: 'auditd', - description: null, - example: null, - format: '', - indexes: ['auditbeat'], - name: 'auditd.data.a1', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'auditd.data.a2': { - category: 'auditd', - description: null, - example: null, - format: '', - indexes: ['auditbeat'], - name: 'auditd.data.a2', - searchable: true, - type: 'string', - aggregatable: true, - }, - }, - }, - client: { - fields: { - 'client.address': { - category: 'client', - description: - 'Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.address', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'client.bytes': { - category: 'client', - description: 'Bytes sent from the client to the server.', - example: '184', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.bytes', - searchable: true, - type: 'number', - aggregatable: true, - }, - 'client.domain': { - category: 'client', - description: 'Client domain.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.domain', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'client.geo.country_iso_code': { - category: 'client', - description: 'Country ISO code.', - example: 'CA', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.geo.country_iso_code', - searchable: true, - type: 'string', - aggregatable: true, - }, - }, - }, - cloud: { - fields: { - 'cloud.account.id': { - category: 'cloud', - description: - 'The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.', - example: '666777888999', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'cloud.account.id', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'cloud.availability_zone': { - category: 'cloud', - description: 'Availability zone in which this host is running.', - example: 'us-east-1c', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'cloud.availability_zone', - searchable: true, - type: 'string', - aggregatable: true, - }, - }, - }, - container: { - fields: { - 'container.id': { - category: 'container', - description: 'Unique container id.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'container.id', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'container.image.name': { - category: 'container', - description: 'Name of the image the container was built on.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'container.image.name', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'container.image.tag': { - category: 'container', - description: 'Container image tag.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'container.image.tag', - searchable: true, - type: 'string', - aggregatable: true, - }, - }, - }, - destination: { - fields: { - 'destination.address': { - category: 'destination', - description: - 'Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.address', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'destination.bytes': { - category: 'destination', - description: 'Bytes sent from the destination to the source.', - example: '184', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.bytes', - searchable: true, - type: 'number', - aggregatable: true, - }, - 'destination.domain': { - category: 'destination', - description: 'Destination domain.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.domain', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'destination.ip': { - aggregatable: true, - category: 'destination', - description: - 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.ip', - searchable: true, - type: 'ip', - }, - 'destination.port': { - aggregatable: true, - category: 'destination', - description: 'Port of the destination.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.port', - searchable: true, - type: 'long', - }, - }, - }, - source: { - fields: { - 'source.ip': { - aggregatable: true, - category: 'source', - description: - 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'source.ip', - searchable: true, - type: 'ip', - }, - 'source.port': { - aggregatable: true, - category: 'source', - description: 'Port of the source.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'source.port', - searchable: true, - type: 'long', - }, - }, - }, - event: { - fields: { - 'event.end': { - aggregatable: true, - category: 'event', - description: - 'event.end contains the date when the event ended or when the activity was last observed.', - example: null, - format: '', - indexes: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ], - name: 'event.end', - searchable: true, - type: 'date', - }, - }, - }, - }, - isLoading: false, - indices: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ], - indicesExists: true, - docValueFields: [ - { - field: '@timestamp', - format: 'date_time', - }, - { - field: 'event.end', - format: 'date_time', - }, - ], - indexPatterns: { - fields: [ - { name: '@timestamp', searchable: true, type: 'date', aggregatable: true }, - { name: 'agent.ephemeral_id', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.hostname', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.id', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.name', searchable: true, type: 'string', aggregatable: true }, - { name: 'auditd.data.a0', searchable: true, type: 'string', aggregatable: true }, - { name: 'auditd.data.a1', searchable: true, type: 'string', aggregatable: true }, - { name: 'auditd.data.a2', searchable: true, type: 'string', aggregatable: true }, - { name: 'client.address', searchable: true, type: 'string', aggregatable: true }, - { name: 'client.bytes', searchable: true, type: 'number', aggregatable: true }, - { name: 'client.domain', searchable: true, type: 'string', aggregatable: true }, - { - name: 'client.geo.country_iso_code', - searchable: true, - type: 'string', - aggregatable: true, - }, - { name: 'cloud.account.id', searchable: true, type: 'string', aggregatable: true }, - { - name: 'cloud.availability_zone', - searchable: true, - type: 'string', - aggregatable: true, - }, - { name: 'container.id', searchable: true, type: 'string', aggregatable: true }, - { - name: 'container.image.name', - searchable: true, - type: 'string', - aggregatable: true, - }, - { name: 'container.image.tag', searchable: true, type: 'string', aggregatable: true }, - { name: 'destination.address', searchable: true, type: 'string', aggregatable: true }, - { name: 'destination.bytes', searchable: true, type: 'number', aggregatable: true }, - { name: 'destination.domain', searchable: true, type: 'string', aggregatable: true }, - { name: 'destination.ip', searchable: true, type: 'ip', aggregatable: true }, - { name: 'destination.port', searchable: true, type: 'long', aggregatable: true }, - { name: 'source.ip', searchable: true, type: 'ip', aggregatable: true }, - { name: 'source.port', searchable: true, type: 'long', aggregatable: true }, - { name: 'event.end', searchable: true, type: 'date', aggregatable: true }, - ], - title: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', - }, - }, - result.current[1], - ]); - }); - }); - - test('unhappy path', async () => { - await act(async () => { - mockUseApolloClient.mockImplementation(() => ({ - query: () => Promise.reject(new Error('Something went wrong')), - })); - const { result, waitForNextUpdate } = renderHook(() => - useFetchIndexPatterns(DEFAULT_INDEX_PATTERN) - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual([ - { - browserFields: {}, - docValueFields: [], - indexPatterns: { - fields: [], - title: '', - }, - indices: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ], - indicesExists: false, - isLoading: false, - }, - result.current[1], - ]); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.tsx deleted file mode 100644 index 82c9292af74515..00000000000000 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.tsx +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { isEmpty, get } from 'lodash/fp'; -import { useEffect, useState, Dispatch, SetStateAction } from 'react'; -import deepEqual from 'fast-deep-equal'; - -import { IIndexPattern } from '../../../../../../../../src/plugins/data/public'; -import { - BrowserFields, - getBrowserFields, - getDocValueFields, - getIndexFields, - sourceQuery, - DocValueFields, -} from '../../../../common/containers/source'; -import { errorToToaster, useStateToaster } from '../../../../common/components/toasters'; -import { SourceQuery } from '../../../../graphql/types'; -import { useApolloClient } from '../../../../common/utils/apollo_context'; - -import * as i18n from './translations'; - -interface FetchIndexPatternReturn { - browserFields: BrowserFields; - docValueFields: DocValueFields[]; - isLoading: boolean; - indices: string[]; - indicesExists: boolean; - indexPatterns: IIndexPattern; -} - -export type Return = [FetchIndexPatternReturn, Dispatch>]; - -const DEFAULT_BROWSER_FIELDS = {}; -const DEFAULT_INDEX_PATTERNS = { fields: [], title: '' }; -const DEFAULT_DOC_VALUE_FIELDS: DocValueFields[] = []; - -// Fun fact: When using this hook multiple times within a component (e.g. add_exception_modal & edit_exception_modal), -// the apolloClient will perform queryDeduplication and prevent the first query from executing. A deep compare is not -// performed on `indices`, so another field must be passed to circumvent this. -// For details, see https://github.com/apollographql/react-apollo/issues/2202 -export const useFetchIndexPatterns = ( - defaultIndices: string[] = [], - queryDeduplication?: string -): Return => { - const apolloClient = useApolloClient(); - const [indices, setIndices] = useState(defaultIndices); - - const [state, setState] = useState({ - browserFields: DEFAULT_BROWSER_FIELDS, - docValueFields: DEFAULT_DOC_VALUE_FIELDS, - indices: defaultIndices, - indicesExists: false, - indexPatterns: DEFAULT_INDEX_PATTERNS, - isLoading: false, - }); - - const [, dispatchToaster] = useStateToaster(); - - useEffect(() => { - if (!deepEqual(defaultIndices, indices)) { - setIndices(defaultIndices); - setState((prevState) => ({ ...prevState, indices: defaultIndices })); - } - }, [defaultIndices, indices]); - - useEffect(() => { - let isSubscribed = true; - const abortCtrl = new AbortController(); - - async function fetchIndexPatterns() { - if (apolloClient && !isEmpty(indices)) { - setState((prevState) => ({ ...prevState, isLoading: true })); - apolloClient - .query({ - query: sourceQuery, - fetchPolicy: 'cache-first', - variables: { - sourceId: 'default', - defaultIndex: indices, - ...(queryDeduplication != null ? { queryDeduplication } : {}), - }, - context: { - fetchOptions: { - signal: abortCtrl.signal, - }, - }, - }) - .then( - (result) => { - if (isSubscribed) { - setState({ - browserFields: getBrowserFields( - indices.join(), - get('data.source.status.indexFields', result) - ), - docValueFields: getDocValueFields( - indices.join(), - get('data.source.status.indexFields', result) - ), - indices, - isLoading: false, - indicesExists: get('data.source.status.indicesExist', result), - indexPatterns: getIndexFields( - indices.join(), - get('data.source.status.indexFields', result) - ), - }); - } - }, - (error) => { - if (isSubscribed) { - setState((prevState) => ({ ...prevState, isLoading: false })); - errorToToaster({ title: i18n.RULE_ADD_FAILURE, error, dispatchToaster }); - } - } - ); - } - } - fetchIndexPatterns(); - return () => { - isSubscribed = false; - abortCtrl.abort(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [indices]); - - return [state, setIndices]; -}; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/index.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/index.ts index a40ab2e4878519..930391261ac873 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/index.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/index.ts @@ -5,7 +5,6 @@ */ export * from './api'; -export * from './fetch_index_patterns'; export * from './use_update_rule'; export * from './use_create_rule'; export * from './types'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx index 8c21f6a1e8cb78..a5d21d28475860 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx @@ -20,7 +20,7 @@ import { import { setAbsoluteRangeDatePicker } from '../../../common/store/inputs/actions'; import { DetectionEnginePageComponent } from './detection_engine'; import { useUserData } from '../../components/user_info'; -import { useWithSource } from '../../../common/containers/source'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; import { createStore, State } from '../../../common/store'; import { mockHistory, Router } from '../../../cases/components/__mock__/router'; @@ -34,7 +34,7 @@ jest.mock('../../../common/components/query_bar', () => ({ })); jest.mock('../../containers/detection_engine/lists/use_lists_config'); jest.mock('../../components/user_info'); -jest.mock('../../../common/containers/source'); +jest.mock('../../../common/containers/sourcerer'); jest.mock('../../../common/components/link_to'); jest.mock('../../../common/containers/use_global_time', () => ({ useGlobalTime: jest.fn().mockReturnValue({ @@ -74,7 +74,7 @@ describe('DetectionEnginePageComponent', () => { beforeAll(() => { (useParams as jest.Mock).mockReturnValue({}); (useUserData as jest.Mock).mockReturnValue([{}]); - (useWithSource as jest.Mock).mockReturnValue({ + (useSourcererScope as jest.Mock).mockReturnValue({ indicesExist: true, indexPattern: {}, }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index 3a3854f145db3d..b39cd375216028 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -13,7 +13,6 @@ import { useHistory } from 'react-router-dom'; import { SecurityPageName } from '../../../app/types'; import { TimelineId } from '../../../../common/types/timeline'; import { useGlobalTime } from '../../../common/containers/use_global_time'; -import { useWithSource } from '../../../common/containers/source'; import { UpdateDateRange } from '../../../common/components/charts/common'; import { FiltersGlobal } from '../../../common/components/filters_global'; import { getRulesUrl } from '../../../common/components/link_to/redirect_to_detection_engine'; @@ -46,6 +45,8 @@ import { timelineSelectors } from '../../../timelines/store/timeline'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; import { TimelineModel } from '../../../timelines/store/timeline/model'; import { buildShowBuildingBlockFilter } from '../../components/alerts_table/default_config'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; +import { SourcererScopeName } from '../../../common/store/sourcerer/model'; export const DetectionEnginePageComponent: React.FC = ({ filters, @@ -117,10 +118,7 @@ export const DetectionEnginePageComponent: React.FC = ({ [setShowBuildingBlockAlerts] ); - const indexToAdd = useMemo(() => (signalIndexName == null ? [] : [signalIndexName]), [ - signalIndexName, - ]); - const { indicesExist, indexPattern } = useWithSource('default', indexToAdd); + const { indicesExist, indexPattern } = useSourcererScope(SourcererScopeName.detections); if (isUserAuthenticated != null && !isUserAuthenticated && !loading) { return ( @@ -202,7 +200,6 @@ export const DetectionEnginePageComponent: React.FC = ({ defaultFilters={alertsTableDefaultFilters} showBuildingBlockAlerts={showBuildingBlockAlerts} onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChangedCallback} - signalsIndex={signalIndexName ?? ''} to={to} /> diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx index f8f9da78b2a062..22c3c43fb23563 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx @@ -20,7 +20,7 @@ import { RuleDetailsPageComponent } from './index'; import { createStore, State } from '../../../../../common/store'; import { setAbsoluteRangeDatePicker } from '../../../../../common/store/inputs/actions'; import { useUserData } from '../../../../components/user_info'; -import { useWithSource } from '../../../../../common/containers/source'; +import { useSourcererScope } from '../../../../../common/containers/sourcerer'; import { useParams } from 'react-router-dom'; import { mockHistory, Router } from '../../../../../cases/components/__mock__/router'; @@ -35,7 +35,7 @@ jest.mock('../../../../../common/components/query_bar', () => ({ jest.mock('../../../../containers/detection_engine/lists/use_lists_config'); jest.mock('../../../../../common/components/link_to'); jest.mock('../../../../components/user_info'); -jest.mock('../../../../../common/containers/source'); +jest.mock('../../../../../common/containers/sourcerer'); jest.mock('../../../../../common/containers/use_global_time', () => ({ useGlobalTime: jest.fn().mockReturnValue({ from: '2020-07-07T08:20:18.966Z', @@ -71,7 +71,7 @@ describe('RuleDetailsPageComponent', () => { beforeAll(() => { (useUserData as jest.Mock).mockReturnValue([{}]); (useParams as jest.Mock).mockReturnValue({}); - (useWithSource as jest.Mock).mockReturnValue({ + (useSourcererScope as jest.Mock).mockReturnValue({ indicesExist: true, indexPattern: {}, }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx index 68799f46eee57c..4816358e06226d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx @@ -36,10 +36,7 @@ import { SiemSearchBar } from '../../../../../common/components/search_bar'; import { WrapperPage } from '../../../../../common/components/wrapper_page'; import { Rule } from '../../../../containers/detection_engine/rules'; import { useListsConfig } from '../../../../containers/detection_engine/lists/use_lists_config'; - -import { useWithSource } from '../../../../../common/containers/source'; import { SpyRoute } from '../../../../../common/utils/route/spy_routes'; - import { StepAboutRuleToggleDetails } from '../../../../components/rules/step_about_rule_details'; import { DetectionEngineHeaderPage } from '../../../../components/detection_engine_header_page'; import { AlertsHistogramPanel } from '../../../../components/alerts_histogram_panel'; @@ -89,6 +86,8 @@ import { showGlobalFilters } from '../../../../../timelines/components/timeline/ import { timelineSelectors } from '../../../../../timelines/store/timeline'; import { timelineDefaults } from '../../../../../timelines/store/timeline/defaults'; import { TimelineModel } from '../../../../../timelines/store/timeline/model'; +import { useSourcererScope } from '../../../../../common/containers/sourcerer'; +import { SourcererScopeName } from '../../../../../common/store/sourcerer/model'; enum RuleDetailTabs { alerts = 'alerts', @@ -265,10 +264,6 @@ export const RuleDetailsPageComponent: FC = ({ [rule, ruleDetailTab] ); - const indexToAdd = useMemo(() => (signalIndexName == null ? [] : [signalIndexName]), [ - signalIndexName, - ]); - const updateDateRangeCallback = useCallback( ({ x }) => { if (!x) { @@ -308,7 +303,7 @@ export const RuleDetailsPageComponent: FC = ({ [setShowBuildingBlockAlerts] ); - const { indicesExist, indexPattern } = useWithSource('default', indexToAdd); + const { indicesExist, indexPattern } = useSourcererScope(SourcererScopeName.detections); const exceptionLists = useMemo((): { lists: ExceptionIdentifiers[]; @@ -500,7 +495,6 @@ export const RuleDetailsPageComponent: FC = ({ loading={loading} showBuildingBlockAlerts={showBuildingBlockAlerts} onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChangedCallback} - signalsIndex={signalIndexName ?? ''} to={to} /> )} diff --git a/x-pack/plugins/security_solution/public/graphql/introspection.json b/x-pack/plugins/security_solution/public/graphql/introspection.json index 9e6a4f21ec64f5..568a960f0804ed 100644 --- a/x-pack/plugins/security_solution/public/graphql/introspection.json +++ b/x-pack/plugins/security_solution/public/graphql/introspection.json @@ -2382,7 +2382,7 @@ "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "IndexField", "ofType": null } + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } } }, @@ -2405,153 +2405,6 @@ "enumValues": null, "possibleTypes": null }, - { - "kind": "OBJECT", - "name": "IndexField", - "description": "A descriptor of a field in an index", - "fields": [ - { - "name": "category", - "description": "Where the field belong", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "example", - "description": "Example of field's value", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "indexes", - "description": "whether the field's belong to an alias index", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "name", - "description": "The name of the field", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "type", - "description": "The type of the field's values as recognized by Kibana", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "searchable", - "description": "Whether the field's values can be efficiently searched for", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "aggregatable", - "description": "Whether the field's values can be aggregated", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "description", - "description": "Description of the field", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "format", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "esTypes", - "description": "the elastic type as mapped in the index", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArrayNoNullable", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "subType", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToIFieldSubTypeNonNullable", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "ToStringArrayNoNullable", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "ToIFieldSubTypeNonNullable", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, { "kind": "INPUT_OBJECT", "name": "TimerangeInput", @@ -9466,6 +9319,22 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "indexNames", + "description": "", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "notes", "description": "", @@ -10933,6 +10802,20 @@ }, "defaultValue": null }, + { + "name": "indexNames", + "description": "", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + }, + "defaultValue": null + }, { "name": "title", "description": "", @@ -12214,6 +12097,16 @@ ], "possibleTypes": null }, + { + "kind": "SCALAR", + "name": "ToStringArrayNoNullable", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", "name": "EcsEdges", @@ -12548,6 +12441,143 @@ ], "possibleTypes": null }, + { + "kind": "SCALAR", + "name": "ToIFieldSubTypeNonNullable", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "IndexField", + "description": "A descriptor of a field in an index", + "fields": [ + { + "name": "category", + "description": "Where the field belong", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "example", + "description": "Example of field's value", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "indexes", + "description": "whether the field's belong to an alias index", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "name", + "description": "The name of the field", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "type", + "description": "The type of the field's values as recognized by Kibana", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "searchable", + "description": "Whether the field's values can be efficiently searched for", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "aggregatable", + "description": "Whether the field's values can be aggregated", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "description", + "description": "Description of the field", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "format", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "esTypes", + "description": "the elastic type as mapped in the index", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArrayNoNullable", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "subType", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToIFieldSubTypeNonNullable", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "ENUM", "name": "FlowDirection", diff --git a/x-pack/plugins/security_solution/public/graphql/types.ts b/x-pack/plugins/security_solution/public/graphql/types.ts index 1699ac4dd33eb9..0bce952912c5c0 100644 --- a/x-pack/plugins/security_solution/public/graphql/types.ts +++ b/x-pack/plugins/security_solution/public/graphql/types.ts @@ -132,6 +132,8 @@ export interface TimelineInput { kqlQuery?: Maybe; + indexNames?: Maybe; + title?: Maybe; templateTimelineId?: Maybe; @@ -413,10 +415,6 @@ export enum FlowDirection { biDirectional = 'biDirectional', } -export type ToStringArrayNoNullable = any; - -export type ToIFieldSubTypeNonNullable = any; - export type ToStringArray = string[]; export type Date = string; @@ -431,6 +429,10 @@ export type ToAny = any; export type EsValue = any; +export type ToStringArrayNoNullable = any; + +export type ToIFieldSubTypeNonNullable = any; + // ==================================================== // Scalars // ==================================================== @@ -589,33 +591,7 @@ export interface SourceStatus { /** Whether the configured alias or wildcard pattern resolve to any auditbeat indices */ indicesExist: boolean; /** The list of fields defined in the index mappings */ - indexFields: IndexField[]; -} - -/** A descriptor of a field in an index */ -export interface IndexField { - /** Where the field belong */ - category: string; - /** Example of field's value */ - example?: Maybe; - /** whether the field's belong to an alias index */ - indexes: (Maybe)[]; - /** The name of the field */ - name: string; - /** The type of the field's values as recognized by Kibana */ - type: string; - /** Whether the field's values can be efficiently searched for */ - searchable: boolean; - /** Whether the field's values can be aggregated */ - aggregatable: boolean; - /** Description of the field */ - description?: Maybe; - - format?: Maybe; - /** the elastic type as mapped in the index */ - esTypes?: Maybe; - - subType?: Maybe; + indexFields: string[]; } export interface AuthenticationsData { @@ -1946,6 +1922,8 @@ export interface TimelineResult { kqlQuery?: Maybe; + indexNames?: Maybe; + notes?: Maybe; noteIds?: Maybe; @@ -2218,6 +2196,32 @@ export interface HostFields { type?: Maybe; } +/** A descriptor of a field in an index */ +export interface IndexField { + /** Where the field belong */ + category: string; + /** Example of field's value */ + example?: Maybe; + /** whether the field's belong to an alias index */ + indexes: (Maybe)[]; + /** The name of the field */ + name: string; + /** The type of the field's values as recognized by Kibana */ + type: string; + /** Whether the field's values can be efficiently searched for */ + searchable: boolean; + /** Whether the field's values can be aggregated */ + aggregatable: boolean; + /** Description of the field */ + description?: Maybe; + + format?: Maybe; + /** the elastic type as mapped in the index */ + esTypes?: Maybe; + + subType?: Maybe; +} + // ==================================================== // Arguments // ==================================================== @@ -2637,61 +2641,6 @@ export namespace GetMatrixHistogramQuery { }; } -export namespace SourceQuery { - export type Variables = { - sourceId?: Maybe; - defaultIndex: string[]; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - status: Status; - }; - - export type Status = { - __typename?: 'SourceStatus'; - - indicesExist: boolean; - - indexFields: IndexFields[]; - }; - - export type IndexFields = { - __typename?: 'IndexField'; - - category: string; - - description: Maybe; - - example: Maybe; - - indexes: (Maybe)[]; - - name: string; - - searchable: boolean; - - type: string; - - aggregatable: boolean; - - format: Maybe; - - esTypes: Maybe; - - subType: Maybe; - }; -} - export namespace GetAuthenticationsQuery { export type Variables = { sourceId: string; @@ -5269,6 +5218,8 @@ export namespace GetOneTimeline { kqlQuery: Maybe; + indexNames: Maybe; + notes: Maybe; noteIds: Maybe; @@ -5601,6 +5552,8 @@ export namespace PersistTimelineMutation { kqlQuery: Maybe; + indexNames: Maybe; + title: Maybe; dateRange: Maybe; diff --git a/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.test.tsx index 606b43c6508fb9..4f64cca45d1625 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.test.tsx @@ -40,7 +40,12 @@ describe('FirstLastSeen Component', () => { useFirstLastSeenHostMock.mockReturnValue([true, MOCKED_RESPONSE]); const { container } = render( - + ); expect(container.innerHTML).toBe( @@ -52,7 +57,12 @@ describe('FirstLastSeen Component', () => { useFirstLastSeenHostMock.mockReturnValue([false, MOCKED_RESPONSE]); const { container } = render( - + ); @@ -69,7 +79,12 @@ describe('FirstLastSeen Component', () => { useFirstLastSeenHostMock.mockReturnValue([false, MOCKED_RESPONSE]); const { container } = render( - + ); await act(() => @@ -91,7 +106,12 @@ describe('FirstLastSeen Component', () => { ]); const { container } = render( - + ); @@ -114,7 +134,12 @@ describe('FirstLastSeen Component', () => { ]); const { container } = render( - + ); @@ -137,7 +162,12 @@ describe('FirstLastSeen Component', () => { ]); const { container } = render( - + ); await act(() => @@ -157,7 +187,12 @@ describe('FirstLastSeen Component', () => { ]); const { container } = render( - + ); await act(() => diff --git a/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.tsx index a1b72fb39069ca..ee415560cf9dec 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.tsx @@ -10,6 +10,7 @@ import React, { useMemo } from 'react'; import { useFirstLastSeenHost } from '../../containers/hosts/first_last_seen'; import { getEmptyTagValue } from '../../../common/components/empty_value'; import { FormattedRelativePreferenceDate } from '../../../common/components/formatted_date'; +import { DocValueFields } from '../../../../common/search_strategy'; export enum FirstLastSeenHostType { FIRST_SEEN = 'first-seen', @@ -17,47 +18,53 @@ export enum FirstLastSeenHostType { } interface FirstLastSeenHostProps { + docValueFields: DocValueFields[]; hostName: string; + indexNames: string[]; type: FirstLastSeenHostType; } -export const FirstLastSeenHost = React.memo(({ hostName, type }) => { - const [loading, { firstSeen, lastSeen, errorMessage }] = useFirstLastSeenHost({ - hostName, - }); - const valueSeen = useMemo( - () => (type === FirstLastSeenHostType.FIRST_SEEN ? firstSeen : lastSeen), - [firstSeen, lastSeen, type] - ); +export const FirstLastSeenHost = React.memo( + ({ docValueFields, hostName, type, indexNames }) => { + const [loading, { firstSeen, lastSeen, errorMessage }] = useFirstLastSeenHost({ + docValueFields, + hostName, + indexNames, + }); + const valueSeen = useMemo( + () => (type === FirstLastSeenHostType.FIRST_SEEN ? firstSeen : lastSeen), + [firstSeen, lastSeen, type] + ); + + if (errorMessage != null) { + return ( + + + + ); + } - if (errorMessage != null) { return ( - - - + <> + {loading && } + {!loading && valueSeen != null && new Date(valueSeen).toString() === 'Invalid Date' + ? valueSeen + : !loading && + valueSeen != null && ( + + + + )} + {!loading && valueSeen == null && getEmptyTagValue()} + ); } - - return ( - <> - {loading && } - {!loading && valueSeen != null && new Date(valueSeen).toString() === 'Invalid Date' - ? valueSeen - : !loading && - valueSeen != null && ( - - - - )} - {!loading && valueSeen == null && getEmptyTagValue()} - - ); -}); +); FirstLastSeenHost.displayName = 'FirstLastSeenHost'; diff --git a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/__snapshots__/index.test.tsx.snap index 3143e680913b2c..1d70f4f72ac8bd 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/__snapshots__/index.test.tsx.snap @@ -68,97 +68,6 @@ exports[`Hosts Table rendering it renders the default Hosts table 1`] = ` } fakeTotalCount={50} id="hostsQuery" - indexPattern={ - Object { - "fields": Array [ - Object { - "aggregatable": true, - "name": "@timestamp", - "searchable": true, - "type": "date", - }, - Object { - "aggregatable": true, - "name": "@version", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.ephemeral_id", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.hostname", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.id", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test1", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test2", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test3", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test4", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test5", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test6", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test7", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test8", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "host.name", - "searchable": true, - "type": "string", - }, - ], - "title": "filebeat-*,auditbeat-*,packetbeat-*", - } - } isInspect={false} loadPage={[MockFunction]} loading={false} diff --git a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx index c4a391687843c1..29e4dc48ae3c75 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx @@ -12,7 +12,6 @@ import { MockedProvider } from 'react-apollo/test-utils'; import '../../../common/mock/match_media'; import { apolloClientObservable, - mockIndexPattern, mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, @@ -69,7 +68,6 @@ describe('Hosts Table', () => { data={mockData.Hosts.edges} id="hostsQuery" isInspect={false} - indexPattern={mockIndexPattern} fakeTotalCount={getOr(50, 'fakeTotalCount', mockData.Hosts.pageInfo)} loading={false} loadPage={loadPage} @@ -92,7 +90,6 @@ describe('Hosts Table', () => { void; @@ -77,7 +75,6 @@ const HostsTableComponent = React.memo( direction, fakeTotalCount, id, - indexPattern, isInspect, limit, loading, diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/authentications/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/authentications/index.tsx index 09496168274701..84003e5dea5e98 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/authentications/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/authentications/index.tsx @@ -42,6 +42,7 @@ export const fieldsMapping: Readonly = [ const HostsKpiAuthenticationsComponent: React.FC = ({ filterQuery, from, + indexNames, to, narrowDateRange, setQuery, @@ -50,6 +51,7 @@ const HostsKpiAuthenticationsComponent: React.FC = ({ const [loading, { refetch, id, inspect, ...data }] = useHostsKpiAuthentications({ filterQuery, endDate: to, + indexNames, startDate: from, skip, }); diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/hosts/index.tsx index b1c4d6331e4504..908ff717e2711b 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/hosts/index.tsx @@ -31,6 +31,7 @@ export const fieldsMapping: Readonly = [ const HostsKpiHostsComponent: React.FC = ({ filterQuery, from, + indexNames, to, narrowDateRange, setQuery, @@ -39,6 +40,7 @@ const HostsKpiHostsComponent: React.FC = ({ const [loading, { refetch, id, inspect, ...data }] = useHostsKpiHosts({ filterQuery, endDate: to, + indexNames, startDate: from, skip, }); diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx index fff4c64900a8bb..6174e174db5a69 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx @@ -13,12 +13,13 @@ import { HostsKpiUniqueIps } from './unique_ips'; import { HostsKpiProps } from './types'; export const HostsKpiComponent = React.memo( - ({ filterQuery, from, to, setQuery, skip, narrowDateRange }) => ( + ({ filterQuery, from, indexNames, to, setQuery, skip, narrowDateRange }) => ( ( ( ( HostsKpiComponent.displayName = 'HostsKpiComponent'; export const HostsDetailsKpiComponent = React.memo( - ({ filterQuery, from, to, setQuery, skip, narrowDateRange }) => ( + ({ filterQuery, from, indexNames, to, setQuery, skip, narrowDateRange }) => ( ( = [ const HostsKpiUniqueIpsComponent: React.FC = ({ filterQuery, from, + indexNames, to, narrowDateRange, setQuery, @@ -50,6 +51,7 @@ const HostsKpiUniqueIpsComponent: React.FC = ({ const [loading, { refetch, id, inspect, ...data }] = useHostsKpiUniqueIps({ filterQuery, endDate: to, + indexNames, startDate: from, skip, }); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.tsx index 7bf4f7a833fb82..b1563e85c93ddf 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.tsx @@ -15,7 +15,6 @@ import { isErrorResponse, } from '../../../../../../../src/plugins/data/common'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { HostsQueries } from '../../../../common/search_strategy/security_solution'; import { HostAuthenticationsRequestOptions, @@ -56,6 +55,7 @@ interface UseAuthentications { docValueFields?: DocValueFields[]; filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; startDate: string; type: hostsModel.HostsType; skip: boolean; @@ -65,6 +65,7 @@ export const useAuthentications = ({ docValueFields, filterQuery, endDate, + indexNames, startDate, type, skip, @@ -74,15 +75,14 @@ export const useAuthentications = ({ (state: State) => getAuthenticationsSelector(state, type), shallowEqual ); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [authenticationsRequest, setAuthenticationsRequest] = useState< HostAuthenticationsRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], factoryQueryType: HostsQueries.authentications, filterQuery: createFilter(filterQuery), @@ -186,7 +186,7 @@ export const useAuthentications = ({ setAuthenticationsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], filterQuery: createFilter(filterQuery), pagination: generateTablePaginationOptions(activePage, limit), @@ -201,7 +201,7 @@ export const useAuthentications = ({ } return prevRequest; }); - }, [activePage, defaultIndex, docValueFields, endDate, filterQuery, limit, skip, startDate]); + }, [activePage, docValueFields, endDate, filterQuery, indexNames, limit, skip, startDate]); useEffect(() => { authenticationsSearch(authenticationsRequest); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/_index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/_index.tsx index f68c340a477236..5b69e20398a35a 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/_index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/_index.tsx @@ -10,7 +10,6 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel } from '../../../../common/store'; import { useKibana } from '../../../../common/lib/kibana'; import { @@ -41,9 +40,10 @@ export interface HostDetailsArgs { } interface UseHostDetails { - id?: string; - hostName: string; endDate: string; + hostName: string; + id?: string; + indexNames: string[]; skip?: boolean; startDate: string; } @@ -51,17 +51,17 @@ interface UseHostDetails { export const useHostDetails = ({ endDate, hostName, + indexNames, + id = ID, skip = false, startDate, - id = ID, }: UseHostDetails): [boolean, HostDetailsArgs] => { - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [hostDetailsRequest, setHostDetailsRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, hostName, factoryQueryType: HostsQueries.details, timerange: { @@ -142,7 +142,7 @@ export const useHostDetails = ({ setHostDetailsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, hostName, timerange: { interval: '12h', @@ -155,7 +155,7 @@ export const useHostDetails = ({ } return prevRequest; }); - }, [defaultIndex, endDate, hostName, startDate, skip]); + }, [endDate, hostName, indexNames, startDate, skip]); useEffect(() => { hostDetailsSearch(hostDetailsRequest); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/index.tsx index 12a82c7980b61b..0236270d186188 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/index.tsx @@ -10,11 +10,9 @@ import { Query } from 'react-apollo'; import { connect } from 'react-redux'; import { compose } from 'redux'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel, inputsSelectors, State } from '../../../../common/store'; import { getDefaultFetchPolicy } from '../../../../common/containers/helpers'; import { QueryTemplate, QueryTemplateProps } from '../../../../common/containers/query_template'; -import { withKibana, WithKibanaProps } from '../../../../common/lib/kibana'; import { HostOverviewQuery } from './host_overview.gql_query'; import { GetHostOverviewQuery, HostItem } from '../../../../graphql/types'; @@ -42,7 +40,7 @@ export interface OwnProps extends QueryTemplateProps { endDate: string; } -type HostsOverViewProps = OwnProps & HostOverviewReduxProps & WithKibanaProps; +type HostsOverViewProps = OwnProps & HostOverviewReduxProps; class HostOverviewByNameComponentQuery extends QueryTemplate< HostsOverViewProps, @@ -52,10 +50,10 @@ class HostOverviewByNameComponentQuery extends QueryTemplate< public render() { const { id = ID, + indexNames, isInspected, children, hostName, - kibana, skip, sourceId, startDate, @@ -75,7 +73,7 @@ class HostOverviewByNameComponentQuery extends QueryTemplate< from: startDate, to: endDate, }, - defaultIndex: kibana.services.uiSettings.get(DEFAULT_INDEX_KEY), + defaultIndex: indexNames, inspect: isInspected, }} > @@ -108,6 +106,5 @@ const makeMapStateToProps = () => { }; export const HostOverviewByNameQuery = compose>( - connect(makeMapStateToProps), - withKibana + connect(makeMapStateToProps) )(HostOverviewByNameComponentQuery); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.tsx index a6376642dfa29e..cc944a59571f19 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.tsx @@ -7,17 +7,15 @@ import deepEqual from 'fast-deep-equal'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; - import { useKibana } from '../../../../common/lib/kibana'; import { HostsQueries, HostFirstLastSeenRequestOptions, HostFirstLastSeenStrategyResponse, } from '../../../../../common/search_strategy/security_solution'; -import { useWithSource } from '../../../../common/containers/source'; import * as i18n from './translations'; +import { DocValueFields } from '../../../../../common/search_strategy'; import { AbortError, isCompleteResponse, @@ -33,21 +31,23 @@ export interface FirstLastSeenHostArgs { lastSeen?: string | null; } interface UseHostFirstLastSeen { + docValueFields: DocValueFields[]; hostName: string; + indexNames: string[]; } export const useFirstLastSeenHost = ({ + docValueFields, hostName, + indexNames, }: UseHostFirstLastSeen): [boolean, FirstLastSeenHostArgs] => { - const { docValueFields } = useWithSource('default'); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [firstLastSeenHostRequest, setFirstLastSeenHostRequest] = useState< HostFirstLastSeenRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], factoryQueryType: HostsQueries.firstLastSeen, hostName, @@ -124,7 +124,7 @@ export const useFirstLastSeenHost = ({ setFirstLastSeenHostRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], hostName, }; @@ -133,7 +133,7 @@ export const useFirstLastSeenHost = ({ } return prevRequest; }); - }, [defaultIndex, docValueFields, hostName]); + }, [indexNames, docValueFields, hostName]); useEffect(() => { firstLastSeenHostSearch(firstLastSeenHostRequest); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx index 2eb926a9733c3d..6ca0272e58d7d6 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx @@ -9,7 +9,6 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useSelector } from 'react-redux'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel, State } from '../../../common/store'; import { createFilter } from '../../../common/containers/helpers'; import { useKibana } from '../../../common/lib/kibana'; @@ -54,6 +53,7 @@ interface UseAllHost { docValueFields?: DocValueFields[]; filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; type: hostsModel.HostsType; @@ -63,6 +63,7 @@ export const useAllHost = ({ docValueFields, filterQuery, endDate, + indexNames, skip = false, startDate, type, @@ -71,13 +72,12 @@ export const useAllHost = ({ const { activePage, direction, limit, sortField } = useSelector((state: State) => getHostsSelector(state, type) ); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [hostsRequest, setHostRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], factoryQueryType: HostsQueries.hosts, filterQuery: createFilter(filterQuery), @@ -181,7 +181,7 @@ export const useAllHost = ({ setHostRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], filterQuery: createFilter(filterQuery), pagination: generateTablePaginationOptions(activePage, limit), @@ -202,11 +202,11 @@ export const useAllHost = ({ }); }, [ activePage, - defaultIndex, direction, docValueFields, endDate, filterQuery, + indexNames, limit, skip, startDate, diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_host_details/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_host_details/index.tsx index 1551e7d7067146..26e4eaf9ea82e0 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/kpi_host_details/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_host_details/index.tsx @@ -9,10 +9,8 @@ import React from 'react'; import { Query } from 'react-apollo'; import { connect, ConnectedProps } from 'react-redux'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { KpiHostDetailsData, GetKpiHostDetailsQuery } from '../../../graphql/types'; import { inputsModel, inputsSelectors, State } from '../../../common/store'; -import { useUiSetting } from '../../../common/lib/kibana'; import { createFilter, getDefaultFetchPolicy } from '../../../common/containers/helpers'; import { QueryTemplateProps } from '../../../common/containers/query_template'; @@ -33,7 +31,17 @@ export interface QueryKpiHostDetailsProps extends QueryTemplateProps { } const KpiHostDetailsComponentQuery = React.memo( - ({ id = ID, children, endDate, filterQuery, isInspected, skip, sourceId, startDate }) => ( + ({ + id = ID, + children, + endDate, + filterQuery, + indexNames, + isInspected, + skip, + sourceId, + startDate, + }) => ( query={kpiHostDetailsQuery} fetchPolicy={getDefaultFetchPolicy()} @@ -47,7 +55,7 @@ const KpiHostDetailsComponentQuery = React.memo(DEFAULT_INDEX_KEY), + defaultIndex: indexNames ?? [], inspect: isInspected, }} > diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/index.tsx index 0d90b73e0a5843..404231be1e6cdb 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/index.tsx @@ -8,7 +8,6 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; import { useKibana } from '../../../../common/lib/kibana'; @@ -37,6 +36,7 @@ export interface HostsKpiAuthenticationsArgs interface UseHostsKpiAuthentications { filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; } @@ -44,18 +44,18 @@ interface UseHostsKpiAuthentications { export const useHostsKpiAuthentications = ({ filterQuery, endDate, + indexNames, skip = false, startDate, }: UseHostsKpiAuthentications): [boolean, HostsKpiAuthenticationsArgs] => { - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [hostsKpiAuthenticationsRequest, setHostsKpiAuthenticationsRequest] = useState< HostsKpiAuthenticationsRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: HostsKpiQueries.kpiAuthentications, filterQuery: createFilter(filterQuery), id: ID, @@ -147,7 +147,7 @@ export const useHostsKpiAuthentications = ({ setHostsKpiAuthenticationsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), timerange: { interval: '12h', @@ -160,7 +160,7 @@ export const useHostsKpiAuthentications = ({ } return prevRequest; }); - }, [defaultIndex, endDate, filterQuery, skip, startDate]); + }, [indexNames, endDate, filterQuery, skip, startDate]); useEffect(() => { hostsKpiAuthenticationsSearch(hostsKpiAuthenticationsRequest); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/index.tsx index 190ce1aa7eae1c..bb918a9214f409 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/index.tsx @@ -8,7 +8,6 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; import { useKibana } from '../../../../common/lib/kibana'; @@ -36,6 +35,7 @@ export interface HostsKpiHostsArgs extends Omit { - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [hostsKpiHostsRequest, setHostsKpiHostsRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: HostsKpiQueries.kpiHosts, filterQuery: createFilter(filterQuery), id: ID, @@ -135,7 +135,7 @@ export const useHostsKpiHosts = ({ setHostsKpiHostsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), timerange: { interval: '12h', @@ -148,7 +148,7 @@ export const useHostsKpiHosts = ({ } return prevRequest; }); - }, [defaultIndex, endDate, filterQuery, skip, startDate]); + }, [indexNames, endDate, filterQuery, skip, startDate]); useEffect(() => { hostsKpiHostsSearch(hostsKpiHostsRequest); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/index.tsx index ac5cc12807f007..b8e93eef8dc914 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/index.tsx @@ -8,7 +8,6 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; import { useKibana } from '../../../../common/lib/kibana'; @@ -37,6 +36,7 @@ export interface HostsKpiUniqueIpsArgs interface UseHostsKpiUniqueIps { filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; } @@ -44,18 +44,18 @@ interface UseHostsKpiUniqueIps { export const useHostsKpiUniqueIps = ({ filterQuery, endDate, + indexNames, skip = false, startDate, }: UseHostsKpiUniqueIps): [boolean, HostsKpiUniqueIpsArgs] => { - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [hostsKpiUniqueIpsRequest, setHostsKpiUniqueIpsRequest] = useState< HostsKpiUniqueIpsRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: HostsKpiQueries.kpiUniqueIps, filterQuery: createFilter(filterQuery), id: ID, @@ -144,7 +144,7 @@ export const useHostsKpiUniqueIps = ({ setHostsKpiUniqueIpsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), timerange: { interval: '12h', @@ -157,7 +157,7 @@ export const useHostsKpiUniqueIps = ({ } return prevRequest; }); - }, [defaultIndex, endDate, filterQuery, skip, startDate]); + }, [indexNames, endDate, filterQuery, skip, startDate]); useEffect(() => { hostsKpiUniqueIpsSearch(hostsKpiUniqueIpsRequest); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx index e28a808378dd74..4036837024025a 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx @@ -15,7 +15,6 @@ import { isErrorResponse, } from '../../../../../../../src/plugins/data/common'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel, State } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; import { generateTablePaginationOptions } from '../../../common/components/paginated_table/helpers'; @@ -53,6 +52,7 @@ interface UseUncommonProcesses { docValueFields?: DocValueFields[]; filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; type: hostsModel.HostsType; @@ -62,6 +62,7 @@ export const useUncommonProcesses = ({ docValueFields, filterQuery, endDate, + indexNames, skip = false, startDate, type, @@ -70,15 +71,14 @@ export const useUncommonProcesses = ({ const { activePage, limit } = useSelector((state: State) => getUncommonProcessesSelector(state, type) ); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [uncommonProcessesRequest, setUncommonProcessesRequest] = useState< HostsUncommonProcessesRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], factoryQueryType: HostsQueries.uncommonProcesses, filterQuery: createFilter(filterQuery), @@ -186,7 +186,7 @@ export const useUncommonProcesses = ({ setUncommonProcessesRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], filterQuery: createFilter(filterQuery), pagination: generateTablePaginationOptions(activePage, limit), @@ -202,7 +202,7 @@ export const useUncommonProcesses = ({ } return prevRequest; }); - }, [activePage, defaultIndex, docValueFields, endDate, filterQuery, limit, skip, startDate]); + }, [activePage, indexNames, docValueFields, endDate, filterQuery, limit, skip, startDate]); useEffect(() => { uncommonProcessesSearch(uncommonProcessesRequest); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx index 11a268c7b64ad4..708c8b2b40b35e 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx @@ -84,6 +84,7 @@ describe('body', () => { setQuery={jest.fn()} setAbsoluteRangeDatePicker={(jest.fn() as unknown) as SetAbsoluteRangeDatePicker} hostDetailsPagePath={hostDetailsPagePath} + indexNames={[]} indexPattern={mockIndexPattern} type={type} pageFilters={mockHostDetailsPageFilters} diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.tsx index 4d4eead0e778aa..284e6e27cf6154 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.tsx @@ -28,12 +28,13 @@ import { export const HostDetailsTabs = React.memo( ({ + detailName, docValueFields, - pageFilters, filterQuery, - detailName, - setAbsoluteRangeDatePicker, + indexNames, indexPattern, + pageFilters, + setAbsoluteRangeDatePicker, hostDetailsPagePath, }) => { const { from, to, isInitializing, deleteQuery, setQuery } = useGlobalTime(); @@ -73,6 +74,7 @@ export const HostDetailsTabs = React.memo( startDate: from, type, indexPattern, + indexNames, hostName: detailName, narrowDateRange, updateDateRange, diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx index 57e1b128ce64de..55b2b529000be1 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx @@ -28,7 +28,6 @@ import { SiemSearchBar } from '../../../common/components/search_bar'; import { WrapperPage } from '../../../common/components/wrapper_page'; import { HostOverviewByNameQuery } from '../../containers/hosts/details'; import { useGlobalTime } from '../../../common/containers/use_global_time'; -import { useWithSource } from '../../../common/containers/source'; import { LastEventIndexKey } from '../../../graphql/types'; import { useKibana } from '../../../common/lib/kibana'; import { convertToBuildEsQuery } from '../../../common/lib/keury'; @@ -51,6 +50,7 @@ import { timelineSelectors } from '../../../timelines/store/timeline'; import { TimelineModel } from '../../../timelines/store/timeline/model'; import { TimelineId } from '../../../../common/types/timeline'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; const HostOverviewManage = manageQuery(HostOverview); @@ -89,7 +89,7 @@ const HostDetailsComponent = React.memo( }, [setAbsoluteRangeDatePicker] ); - const { docValueFields, indicesExist, indexPattern } = useWithSource(); + const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererScope(); const filterQuery = convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(kibana.services.uiSettings), indexPattern, @@ -111,12 +111,18 @@ const HostDetailsComponent = React.memo( + } title={detailName} /> ( > {({ isLoadingAnomaliesData, anomaliesData }) => ( ( data={hostOverview as HostItem} anomaliesData={anomaliesData} isLoadingAnomaliesData={isLoadingAnomaliesData} + indexNames={selectedPatterns} loading={loading} startDate={from} endDate={to} @@ -161,6 +169,7 @@ const HostDetailsComponent = React.memo( ( ; export type HostDetailsTabsProps = HostBodyComponentDispatchProps & HostsQueryProps & { docValueFields?: DocValueFields[]; + indexNames: string[]; pageFilters?: Filter[]; filterQuery: string; indexPattern: IIndexPattern; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx index 566f8f23efd39f..b341647afdfbc4 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx @@ -10,7 +10,6 @@ import { Router } from 'react-router-dom'; import { Filter } from '../../../../../../src/plugins/data/common/es_query'; import '../../common/mock/match_media'; -import { useWithSource } from '../../common/containers/source'; import { apolloClientObservable, TestProviders, @@ -25,8 +24,9 @@ import { State, createStore } from '../../common/store'; import { HostsComponentProps } from './types'; import { Hosts } from './hosts'; import { HostsTabs } from './hosts_tabs'; +import { useSourcererScope } from '../../common/containers/sourcerer'; -jest.mock('../../common/containers/source'); +jest.mock('../../common/containers/sourcerer'); // Test will fail because we will to need to mock some core services to make the test work // For now let's forget about SiemSearchBar and QueryBar @@ -58,14 +58,14 @@ const mockHistory = { createHref: jest.fn(), listen: jest.fn(), }; - +const mockUseSourcererScope = useSourcererScope as jest.Mock; describe('Hosts - rendering', () => { const hostProps: HostsComponentProps = { hostsPagePath: '', }; test('it renders the Setup Instructions text when no index is available', async () => { - (useWithSource as jest.Mock).mockReturnValue({ + mockUseSourcererScope.mockReturnValue({ indicesExist: false, }); @@ -80,7 +80,7 @@ describe('Hosts - rendering', () => { }); test('it DOES NOT render the Setup Instructions text when an index is available', async () => { - (useWithSource as jest.Mock).mockReturnValue({ + mockUseSourcererScope.mockReturnValue({ indicesExist: true, indexPattern: {}, }); @@ -95,7 +95,7 @@ describe('Hosts - rendering', () => { }); test('it should render tab navigation', async () => { - (useWithSource as jest.Mock).mockReturnValue({ + mockUseSourcererScope.mockReturnValue({ indicesExist: true, indexPattern: {}, }); @@ -142,7 +142,7 @@ describe('Hosts - rendering', () => { }, }, ]; - (useWithSource as jest.Mock).mockReturnValue({ + mockUseSourcererScope.mockReturnValue({ indicesExist: true, indexPattern: { fields: [], title: 'title' }, }); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx index 4b8e3cc6987ac8..ea8cf11e7595a9 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx @@ -22,7 +22,6 @@ import { SiemSearchBar } from '../../common/components/search_bar'; import { WrapperPage } from '../../common/components/wrapper_page'; import { useFullScreen } from '../../common/containers/use_full_screen'; import { useGlobalTime } from '../../common/containers/use_global_time'; -import { useWithSource } from '../../common/containers/source'; import { TimelineId } from '../../../common/types/timeline'; import { LastEventIndexKey } from '../../graphql/types'; import { useKibana } from '../../common/lib/kibana'; @@ -46,6 +45,7 @@ import { showGlobalFilters } from '../../timelines/components/timeline/helpers'; import { timelineSelectors } from '../../timelines/store/timeline'; import { timelineDefaults } from '../../timelines/store/timeline/defaults'; import { TimelineModel } from '../../timelines/store/timeline/model'; +import { useSourcererScope } from '../../common/containers/sourcerer'; export const HostsComponent = React.memo( ({ filters, graphEventId, query, setAbsoluteRangeDatePicker, hostsPagePath }) => { @@ -74,7 +74,7 @@ export const HostsComponent = React.memo( }, [setAbsoluteRangeDatePicker] ); - const { docValueFields, indicesExist, indexPattern } = useWithSource(); + const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererScope(); const filterQuery = convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(kibana.services.uiSettings), indexPattern, @@ -101,12 +101,19 @@ export const HostsComponent = React.memo( } + subtitle={ + + } title={i18n.PAGE_TITLE} /> ( to={to} filterQuery={tabsFilterQuery} isInitializing={isInitializing} + indexNames={selectedPatterns} setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker} setQuery={setQuery} from={from} type={hostsModel.HostsType.page} - indexPattern={indexPattern} hostsPagePath={hostsPagePath} /> diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx index 8e2ea06fd20cbb..17dd20bac2d0d7 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx @@ -28,24 +28,24 @@ export const HostsTabs = memo( deleteQuery, docValueFields, filterQuery, - setAbsoluteRangeDatePicker, - to, from, - setQuery, + indexNames, isInitializing, - type, - indexPattern, hostsPagePath, + setAbsoluteRangeDatePicker, + setQuery, + to, + type, }) => { const tabProps = { deleteQuery, endDate: to, filterQuery, + indexNames, skip: isInitializing, setQuery, startDate: from, type, - indexPattern, narrowDateRange: useCallback( (score: Anomaly, interval: string) => { const fromTo = scoreIntervalToDateTime(score, interval); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/authentications_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/authentications_query_tab_body.tsx index d3fc68874ce912..efce312fd85f2c 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/authentications_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/authentications_query_tab_body.tsx @@ -66,6 +66,7 @@ const AuthenticationsQueryTabBodyComponent: React.FC docValueFields, endDate, filterQuery, + indexNames, skip, setQuery, startDate, @@ -74,7 +75,15 @@ const AuthenticationsQueryTabBodyComponent: React.FC const [ loading, { authentications, totalCount, pageInfo, loadPage, id, inspect, isInspected, refetch }, - ] = useAuthentications({ docValueFields, endDate, filterQuery, skip, startDate, type }); + ] = useAuthentications({ + docValueFields, + endDate, + filterQuery, + indexNames, + skip, + startDate, + type, + }); useEffect(() => { return () => { @@ -90,6 +99,7 @@ const AuthenticationsQueryTabBodyComponent: React.FC endDate={endDate} filterQuery={filterQuery} id={ID} + indexNames={indexNames} setQuery={setQuery} startDate={startDate} {...histogramConfigs} diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx index be8412caf77326..e30071ec04f0cf 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx @@ -20,6 +20,7 @@ import { useFullScreen } from '../../../common/containers/use_full_screen'; import * as i18n from '../translations'; import { MatrixHistogramType } from '../../../../common/search_strategy/security_solution'; import { useManageTimeline } from '../../../timelines/components/manage_timeline'; +import { SourcererScopeName } from '../../../common/store/sourcerer/model'; const EVENTS_HISTOGRAM_ID = 'eventsHistogramQuery'; @@ -54,6 +55,7 @@ const EventsQueryTabBodyComponent: React.FC = ({ deleteQuery, endDate, filterQuery, + indexNames, pageFilters, setQuery, startDate, @@ -85,6 +87,7 @@ const EventsQueryTabBodyComponent: React.FC = ({ setQuery={setQuery} startDate={startDate} id={EVENTS_HISTOGRAM_ID} + indexNames={indexNames} {...histogramConfigs} /> )} @@ -92,6 +95,7 @@ const EventsQueryTabBodyComponent: React.FC = ({ defaultModel={eventsDefaultModel} end={endDate} id={TimelineId.hostsPageEvents} + scopeId={SourcererScopeName.default} start={startDate} pageFilters={pageFilters} /> diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/hosts_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/hosts_query_tab_body.tsx index f8dcf9635c053e..deda4b618fa644 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/hosts_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/hosts_query_tab_body.tsx @@ -18,7 +18,7 @@ export const HostsQueryTabBody = ({ docValueFields, endDate, filterQuery, - indexPattern, + indexNames, skip, setQuery, startDate, @@ -27,7 +27,7 @@ export const HostsQueryTabBody = ({ const [ loading, { hosts, totalCount, pageInfo, loadPage, id, inspect, isInspected, refetch }, - ] = useAllHost({ docValueFields, endDate, filterQuery, skip, startDate, type }); + ] = useAllHost({ docValueFields, endDate, filterQuery, indexNames, skip, startDate, type }); return ( - + {actions} diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index 235f1506371163..40c982cfc071bf 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -203,6 +203,7 @@ export const PolicyDetails = React.memo(() => { )}
+ +
+
+ +
+
+
+ +
+
+ +
+
+
{ reactTestingLibrary.act(() => { history.push('/trusted_apps'); }); + window.scrollTo = jest.fn(); }); test.skip('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/kpi_network/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/network/components/kpi_network/__snapshots__/index.test.tsx.snap index a03d7c2317517d..c512bd99a79165 100644 --- a/x-pack/plugins/security_solution/public/network/components/kpi_network/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/network/components/kpi_network/__snapshots__/index.test.tsx.snap @@ -4,6 +4,7 @@ exports[`NetworkKpiComponent rendering it renders the default widget 1`] = ` = [ const NetworkKpiDnsComponent: React.FC = ({ filterQuery, from, + indexNames, to, narrowDateRange, setQuery, @@ -36,6 +37,7 @@ const NetworkKpiDnsComponent: React.FC = ({ const [loading, { refetch, id, inspect, ...data }] = useNetworkKpiDns({ filterQuery, endDate: to, + indexNames, startDate: from, skip, }); diff --git a/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx index 9c6b2fe3c8ca46..25a9fe03f9205f 100644 --- a/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx @@ -22,12 +22,13 @@ import { NetworkKpiComponent } from '.'; describe('NetworkKpiComponent', () => { const state: State = mockGlobalState; const props = { + filterQuery: '', from: '2019-06-15T06:00:00.000Z', - to: '2019-06-18T06:00:00.000Z', + indexNames: [], narrowDateRange: jest.fn(), - filterQuery: '', setQuery: jest.fn(), skip: true, + to: '2019-06-18T06:00:00.000Z', }; const { storage } = createSecuritySolutionStorageMock(); diff --git a/x-pack/plugins/security_solution/public/network/components/kpi_network/index.tsx b/x-pack/plugins/security_solution/public/network/components/kpi_network/index.tsx index 95534e1a61988b..1a04d1cc2c0eb8 100644 --- a/x-pack/plugins/security_solution/public/network/components/kpi_network/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/kpi_network/index.tsx @@ -15,7 +15,7 @@ import { NetworkKpiUniquePrivateIps } from './unique_private_ips'; import { NetworkKpiProps } from './types'; export const NetworkKpiComponent = React.memo( - ({ filterQuery, from, to, setQuery, skip, narrowDateRange }) => ( + ({ filterQuery, from, indexNames, to, setQuery, skip, narrowDateRange }) => ( @@ -23,6 +23,7 @@ export const NetworkKpiComponent = React.memo( ( ( ( ( = [ const NetworkKpiNetworkEventsComponent: React.FC = ({ filterQuery, from, + indexNames, to, narrowDateRange, setQuery, @@ -41,6 +42,7 @@ const NetworkKpiNetworkEventsComponent: React.FC = ({ const [loading, { refetch, id, inspect, ...data }] = useNetworkKpiNetworkEvents({ filterQuery, endDate: to, + indexNames, startDate: from, skip, }); diff --git a/x-pack/plugins/security_solution/public/network/components/kpi_network/tls_handshakes/index.tsx b/x-pack/plugins/security_solution/public/network/components/kpi_network/tls_handshakes/index.tsx index 575d4256e83956..500314446bc12a 100644 --- a/x-pack/plugins/security_solution/public/network/components/kpi_network/tls_handshakes/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/kpi_network/tls_handshakes/index.tsx @@ -28,6 +28,7 @@ export const fieldsMapping: Readonly = [ const NetworkKpiTlsHandshakesComponent: React.FC = ({ filterQuery, from, + indexNames, to, narrowDateRange, setQuery, @@ -36,6 +37,7 @@ const NetworkKpiTlsHandshakesComponent: React.FC = ({ const [loading, { refetch, id, inspect, ...data }] = useNetworkKpiTlsHandshakes({ filterQuery, endDate: to, + indexNames, startDate: from, skip, }); diff --git a/x-pack/plugins/security_solution/public/network/components/kpi_network/types.ts b/x-pack/plugins/security_solution/public/network/components/kpi_network/types.ts index d3a0ac5a6c5dd5..860e6e228bbaa4 100644 --- a/x-pack/plugins/security_solution/public/network/components/kpi_network/types.ts +++ b/x-pack/plugins/security_solution/public/network/components/kpi_network/types.ts @@ -10,6 +10,7 @@ import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; export interface NetworkKpiProps { filterQuery: string; from: string; + indexNames: string[]; to: string; narrowDateRange: UpdateDateRange; setQuery: GlobalTimeArgs['setQuery']; diff --git a/x-pack/plugins/security_solution/public/network/components/kpi_network/unique_flows/index.tsx b/x-pack/plugins/security_solution/public/network/components/kpi_network/unique_flows/index.tsx index d22d4454952f9c..65624ba4813785 100644 --- a/x-pack/plugins/security_solution/public/network/components/kpi_network/unique_flows/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/kpi_network/unique_flows/index.tsx @@ -28,6 +28,7 @@ export const fieldsMapping: Readonly = [ const NetworkKpiUniqueFlowsComponent: React.FC = ({ filterQuery, from, + indexNames, to, narrowDateRange, setQuery, @@ -36,6 +37,7 @@ const NetworkKpiUniqueFlowsComponent: React.FC = ({ const [loading, { refetch, id, inspect, ...data }] = useNetworkKpiUniqueFlows({ filterQuery, endDate: to, + indexNames, startDate: from, skip, }); diff --git a/x-pack/plugins/security_solution/public/network/components/kpi_network/unique_private_ips/index.tsx b/x-pack/plugins/security_solution/public/network/components/kpi_network/unique_private_ips/index.tsx index a7dfb38219c070..a8a179b97f51a2 100644 --- a/x-pack/plugins/security_solution/public/network/components/kpi_network/unique_private_ips/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/kpi_network/unique_private_ips/index.tsx @@ -47,6 +47,7 @@ export const fieldsMapping: Readonly = [ const NetworkKpiUniquePrivateIpsComponent: React.FC = ({ filterQuery, from, + indexNames, to, narrowDateRange, setQuery, @@ -55,6 +56,7 @@ const NetworkKpiUniquePrivateIpsComponent: React.FC = ({ const [loading, { refetch, id, inspect, ...data }] = useNetworkKpiUniquePrivateIps({ filterQuery, endDate: to, + indexNames, startDate: from, skip, }); diff --git a/x-pack/plugins/security_solution/public/network/containers/details/index.tsx b/x-pack/plugins/security_solution/public/network/containers/details/index.tsx index f6ea86bd552f44..217241bdadcbb3 100644 --- a/x-pack/plugins/security_solution/public/network/containers/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/details/index.tsx @@ -9,7 +9,6 @@ import { useState, useEffect, useCallback, useRef } from 'react'; import deepEqual from 'fast-deep-equal'; import { ESTermQuery } from '../../../../common/typed_json'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; import { createFilter } from '../../../common/containers/helpers'; @@ -42,6 +41,7 @@ interface UseNetworkDetails { id?: string; docValueFields: DocValueFields[]; ip: string; + indexNames: string[]; filterQuery?: ESTermQuery | string; skip: boolean; } @@ -49,18 +49,18 @@ interface UseNetworkDetails { export const useNetworkDetails = ({ docValueFields, filterQuery, + indexNames, id = ID, skip, ip, }: UseNetworkDetails): [boolean, NetworkDetailsArgs] => { - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [networkDetailsRequest, setNetworkDetailsRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], factoryQueryType: NetworkQueries.details, filterQuery: createFilter(filterQuery), @@ -137,7 +137,7 @@ export const useNetworkDetails = ({ setNetworkDetailsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, ip, docValueFields: docValueFields ?? [], filterQuery: createFilter(filterQuery), @@ -147,7 +147,7 @@ export const useNetworkDetails = ({ } return prevRequest; }); - }, [defaultIndex, filterQuery, skip, ip, docValueFields]); + }, [indexNames, filterQuery, skip, ip, docValueFields]); useEffect(() => { networkDetailsSearch(networkDetailsRequest); diff --git a/x-pack/plugins/security_solution/public/network/containers/kpi_network/dns/index.tsx b/x-pack/plugins/security_solution/public/network/containers/kpi_network/dns/index.tsx index 2afbff3138c6b6..dc60bb0a82ba8f 100644 --- a/x-pack/plugins/security_solution/public/network/containers/kpi_network/dns/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/kpi_network/dns/index.tsx @@ -8,7 +8,6 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; import { useKibana } from '../../../../common/lib/kibana'; @@ -41,6 +40,7 @@ export interface NetworkKpiDnsArgs { interface UseNetworkKpiDns { filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; } @@ -48,16 +48,16 @@ interface UseNetworkKpiDns { export const useNetworkKpiDns = ({ filterQuery, endDate, + indexNames, skip = false, startDate, }: UseNetworkKpiDns): [boolean, NetworkKpiDnsArgs] => { - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [networkKpiDnsRequest, setNetworkKpiDnsRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: NetworkKpiQueries.dns, filterQuery: createFilter(filterQuery), id: ID, @@ -138,7 +138,7 @@ export const useNetworkKpiDns = ({ setNetworkKpiDnsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), timerange: { interval: '12h', @@ -151,7 +151,7 @@ export const useNetworkKpiDns = ({ } return prevRequest; }); - }, [defaultIndex, endDate, filterQuery, skip, startDate]); + }, [indexNames, endDate, filterQuery, skip, startDate]); useEffect(() => { networkKpiDnsSearch(networkKpiDnsRequest); diff --git a/x-pack/plugins/security_solution/public/network/containers/kpi_network/network_events/index.tsx b/x-pack/plugins/security_solution/public/network/containers/kpi_network/network_events/index.tsx index 26b57ef36b09d0..a1727d5bb4331b 100644 --- a/x-pack/plugins/security_solution/public/network/containers/kpi_network/network_events/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/kpi_network/network_events/index.tsx @@ -8,7 +8,6 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; import { useKibana } from '../../../../common/lib/kibana'; @@ -41,6 +40,7 @@ export interface NetworkKpiNetworkEventsArgs { interface UseNetworkKpiNetworkEvents { filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; } @@ -48,18 +48,18 @@ interface UseNetworkKpiNetworkEvents { export const useNetworkKpiNetworkEvents = ({ filterQuery, endDate, + indexNames, skip = false, startDate, }: UseNetworkKpiNetworkEvents): [boolean, NetworkKpiNetworkEventsArgs] => { - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [networkKpiNetworkEventsRequest, setNetworkKpiNetworkEventsRequest] = useState< NetworkKpiNetworkEventsRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: NetworkKpiQueries.networkEvents, filterQuery: createFilter(filterQuery), id: ID, @@ -145,7 +145,7 @@ export const useNetworkKpiNetworkEvents = ({ setNetworkKpiNetworkEventsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), timerange: { interval: '12h', @@ -158,7 +158,7 @@ export const useNetworkKpiNetworkEvents = ({ } return prevRequest; }); - }, [defaultIndex, endDate, filterQuery, skip, startDate]); + }, [indexNames, endDate, filterQuery, skip, startDate]); useEffect(() => { networkKpiNetworkEventsSearch(networkKpiNetworkEventsRequest); diff --git a/x-pack/plugins/security_solution/public/network/containers/kpi_network/tls_handshakes/index.tsx b/x-pack/plugins/security_solution/public/network/containers/kpi_network/tls_handshakes/index.tsx index c97c1e43e699ac..bcbe485e821636 100644 --- a/x-pack/plugins/security_solution/public/network/containers/kpi_network/tls_handshakes/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/kpi_network/tls_handshakes/index.tsx @@ -8,7 +8,6 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; import { useKibana } from '../../../../common/lib/kibana'; @@ -41,6 +40,7 @@ export interface NetworkKpiTlsHandshakesArgs { interface UseNetworkKpiTlsHandshakes { filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; } @@ -48,18 +48,18 @@ interface UseNetworkKpiTlsHandshakes { export const useNetworkKpiTlsHandshakes = ({ filterQuery, endDate, + indexNames, skip = false, startDate, }: UseNetworkKpiTlsHandshakes): [boolean, NetworkKpiTlsHandshakesArgs] => { - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [networkKpiTlsHandshakesRequest, setNetworkKpiTlsHandshakesRequest] = useState< NetworkKpiTlsHandshakesRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: NetworkKpiQueries.tlsHandshakes, filterQuery: createFilter(filterQuery), id: ID, @@ -145,7 +145,7 @@ export const useNetworkKpiTlsHandshakes = ({ setNetworkKpiTlsHandshakesRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), timerange: { interval: '12h', @@ -158,7 +158,7 @@ export const useNetworkKpiTlsHandshakes = ({ } return prevRequest; }); - }, [defaultIndex, endDate, filterQuery, skip, startDate]); + }, [indexNames, endDate, filterQuery, skip, startDate]); useEffect(() => { networkKpiTlsHandshakesSearch(networkKpiTlsHandshakesRequest); diff --git a/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_flows/index.tsx b/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_flows/index.tsx index 4e8b4ad38b711c..a4fdefc93fe75c 100644 --- a/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_flows/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_flows/index.tsx @@ -8,7 +8,6 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; import { useKibana } from '../../../../common/lib/kibana'; @@ -41,6 +40,7 @@ export interface NetworkKpiUniqueFlowsArgs { interface UseNetworkKpiUniqueFlows { filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; } @@ -48,18 +48,18 @@ interface UseNetworkKpiUniqueFlows { export const useNetworkKpiUniqueFlows = ({ filterQuery, endDate, + indexNames, skip = false, startDate, }: UseNetworkKpiUniqueFlows): [boolean, NetworkKpiUniqueFlowsArgs] => { - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [networkKpiUniqueFlowsRequest, setNetworkKpiUniqueFlowsRequest] = useState< NetworkKpiUniqueFlowsRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: NetworkKpiQueries.uniqueFlows, filterQuery: createFilter(filterQuery), id: ID, @@ -145,7 +145,7 @@ export const useNetworkKpiUniqueFlows = ({ setNetworkKpiUniqueFlowsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), timerange: { interval: '12h', @@ -158,7 +158,7 @@ export const useNetworkKpiUniqueFlows = ({ } return prevRequest; }); - }, [defaultIndex, endDate, filterQuery, skip, startDate]); + }, [indexNames, endDate, filterQuery, skip, startDate]); useEffect(() => { networkKpiUniqueFlowsSearch(networkKpiUniqueFlowsRequest); diff --git a/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_private_ips/index.tsx b/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_private_ips/index.tsx index b518f952121293..5e9d829077f237 100644 --- a/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_private_ips/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/kpi_network/unique_private_ips/index.tsx @@ -8,7 +8,6 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel } from '../../../../common/store'; import { createFilter } from '../../../../common/containers/helpers'; import { useKibana } from '../../../../common/lib/kibana'; @@ -45,6 +44,7 @@ export interface NetworkKpiUniquePrivateIpsArgs { interface UseNetworkKpiUniquePrivateIps { filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; } @@ -52,18 +52,18 @@ interface UseNetworkKpiUniquePrivateIps { export const useNetworkKpiUniquePrivateIps = ({ filterQuery, endDate, + indexNames, skip = false, startDate, }: UseNetworkKpiUniquePrivateIps): [boolean, NetworkKpiUniquePrivateIpsArgs] => { - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [networkKpiUniquePrivateIpsRequest, setNetworkKpiUniquePrivateIpsRequest] = useState< NetworkKpiUniquePrivateIpsRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: NetworkKpiQueries.uniquePrivateIps, filterQuery: createFilter(filterQuery), id: ID, @@ -156,7 +156,7 @@ export const useNetworkKpiUniquePrivateIps = ({ setNetworkKpiUniquePrivateIpsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), timerange: { interval: '12h', @@ -169,7 +169,7 @@ export const useNetworkKpiUniquePrivateIps = ({ } return prevRequest; }); - }, [defaultIndex, endDate, filterQuery, skip, startDate]); + }, [indexNames, endDate, filterQuery, skip, startDate]); useEffect(() => { networkKpiUniquePrivateIpsSearch(networkKpiUniquePrivateIpsRequest); diff --git a/x-pack/plugins/security_solution/public/network/containers/network_dns/index.tsx b/x-pack/plugins/security_solution/public/network/containers/network_dns/index.tsx index 209f8da0d8fae1..334373c4a551a7 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_dns/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_dns/index.tsx @@ -10,7 +10,6 @@ import { shallowEqual, useSelector } from 'react-redux'; import deepEqual from 'fast-deep-equal'; import { ESTermQuery } from '../../../../common/typed_json'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel, State } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; import { createFilter } from '../../../common/containers/helpers'; @@ -51,6 +50,7 @@ export interface NetworkDnsArgs { interface UseNetworkDns { id?: string; + indexNames: string[]; type: networkModel.NetworkType; filterQuery?: ESTermQuery | string; endDate: string; @@ -62,6 +62,7 @@ export const useNetworkDns = ({ endDate, filterQuery, id = ID, + indexNames, skip, startDate, type, @@ -71,14 +72,13 @@ export const useNetworkDns = ({ (state: State) => getNetworkDnsSelector(state), shallowEqual ); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [networkDnsRequest, setNetworkDnsRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: NetworkQueries.dns, filterQuery: createFilter(filterQuery), isPtrIncluded, @@ -182,7 +182,7 @@ export const useNetworkDns = ({ setNetworkDnsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, isPtrIncluded, filterQuery: createFilter(filterQuery), pagination: generateTablePaginationOptions(activePage, limit), @@ -198,7 +198,7 @@ export const useNetworkDns = ({ } return prevRequest; }); - }, [activePage, defaultIndex, endDate, filterQuery, limit, startDate, sort, skip, isPtrIncluded]); + }, [activePage, indexNames, endDate, filterQuery, limit, startDate, sort, skip, isPtrIncluded]); useEffect(() => { networkDnsSearch(networkDnsRequest); diff --git a/x-pack/plugins/security_solution/public/network/containers/network_http/index.tsx b/x-pack/plugins/security_solution/public/network/containers/network_http/index.tsx index 9244d571bb67ba..221b693818c503 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_http/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_http/index.tsx @@ -10,7 +10,6 @@ import { shallowEqual, useSelector } from 'react-redux'; import deepEqual from 'fast-deep-equal'; import { ESTermQuery } from '../../../../common/typed_json'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel, State } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; import { createFilter } from '../../../common/containers/helpers'; @@ -49,6 +48,7 @@ export interface NetworkHttpArgs { interface UseNetworkHttp { id?: string; ip?: string; + indexNames: string[]; type: networkModel.NetworkType; filterQuery?: ESTermQuery | string; endDate: string; @@ -60,6 +60,7 @@ export const useNetworkHttp = ({ endDate, filterQuery, id = ID, + indexNames, ip, skip, startDate, @@ -70,14 +71,13 @@ export const useNetworkHttp = ({ (state: State) => getHttpSelector(state, type), shallowEqual ); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [networkHttpRequest, setHostRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: NetworkQueries.http, filterQuery: createFilter(filterQuery), ip, @@ -181,7 +181,7 @@ export const useNetworkHttp = ({ setHostRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), pagination: generateTablePaginationOptions(activePage, limit), sort: sort as SortField, @@ -196,7 +196,7 @@ export const useNetworkHttp = ({ } return prevRequest; }); - }, [activePage, defaultIndex, endDate, filterQuery, limit, startDate, sort, skip]); + }, [activePage, indexNames, endDate, filterQuery, limit, startDate, sort, skip]); useEffect(() => { networkHttpSearch(networkHttpRequest); diff --git a/x-pack/plugins/security_solution/public/network/containers/network_top_countries/index.tsx b/x-pack/plugins/security_solution/public/network/containers/network_top_countries/index.tsx index 8138d30f2c510b..6b52966342e973 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_top_countries/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_top_countries/index.tsx @@ -10,7 +10,6 @@ import { shallowEqual, useSelector } from 'react-redux'; import deepEqual from 'fast-deep-equal'; import { ESTermQuery } from '../../../../common/typed_json'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel, State } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; import { createFilter } from '../../../common/containers/helpers'; @@ -49,6 +48,7 @@ export interface NetworkTopCountriesArgs { interface UseNetworkTopCountries { flowTarget: FlowTargetSourceDest; ip?: string; + indexNames: string[]; type: networkModel.NetworkType; filterQuery?: ESTermQuery | string; endDate: string; @@ -60,6 +60,7 @@ export const useNetworkTopCountries = ({ endDate, filterQuery, flowTarget, + indexNames, skip, startDate, type, @@ -69,14 +70,13 @@ export const useNetworkTopCountries = ({ (state: State) => getTopCountriesSelector(state, type, flowTarget), shallowEqual ); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [networkTopCountriesRequest, setHostRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: NetworkQueries.topCountries, filterQuery: createFilter(filterQuery), flowTarget, @@ -180,7 +180,7 @@ export const useNetworkTopCountries = ({ setHostRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), pagination: generateTablePaginationOptions(activePage, limit), sort, @@ -195,7 +195,7 @@ export const useNetworkTopCountries = ({ } return prevRequest; }); - }, [activePage, defaultIndex, endDate, filterQuery, limit, startDate, sort, skip]); + }, [activePage, indexNames, endDate, filterQuery, limit, startDate, sort, skip]); useEffect(() => { networkTopCountriesSearch(networkTopCountriesRequest); diff --git a/x-pack/plugins/security_solution/public/network/containers/network_top_n_flow/index.tsx b/x-pack/plugins/security_solution/public/network/containers/network_top_n_flow/index.tsx index 76c2ae2871a386..d6dd14b3259f04 100644 --- a/x-pack/plugins/security_solution/public/network/containers/network_top_n_flow/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/network_top_n_flow/index.tsx @@ -10,7 +10,6 @@ import { shallowEqual, useSelector } from 'react-redux'; import deepEqual from 'fast-deep-equal'; import { ESTermQuery } from '../../../../common/typed_json'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel, State } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; import { createFilter } from '../../../common/containers/helpers'; @@ -49,6 +48,7 @@ export interface NetworkTopNFlowArgs { interface UseNetworkTopNFlow { flowTarget: FlowTargetSourceDest; ip?: string; + indexNames: string[]; type: networkModel.NetworkType; filterQuery?: ESTermQuery | string; endDate: string; @@ -60,6 +60,7 @@ export const useNetworkTopNFlow = ({ endDate, filterQuery, flowTarget, + indexNames, skip, startDate, type, @@ -69,14 +70,13 @@ export const useNetworkTopNFlow = ({ (state: State) => getTopNFlowSelector(state, type, flowTarget), shallowEqual ); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [networkTopNFlowRequest, setTopNFlowRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: NetworkQueries.topNFlow, filterQuery: createFilter(filterQuery), flowTarget, @@ -178,7 +178,7 @@ export const useNetworkTopNFlow = ({ setTopNFlowRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), pagination: generateTablePaginationOptions(activePage, limit), timerange: { @@ -193,7 +193,7 @@ export const useNetworkTopNFlow = ({ } return prevRequest; }); - }, [activePage, defaultIndex, endDate, filterQuery, limit, startDate, sort, skip]); + }, [activePage, indexNames, endDate, filterQuery, limit, startDate, sort, skip]); useEffect(() => { networkTopNFlowSearch(networkTopNFlowRequest); diff --git a/x-pack/plugins/security_solution/public/network/containers/tls/index.tsx b/x-pack/plugins/security_solution/public/network/containers/tls/index.tsx index 4c9658aa9b42ca..f40675a1255ff8 100644 --- a/x-pack/plugins/security_solution/public/network/containers/tls/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/tls/index.tsx @@ -10,7 +10,6 @@ import { shallowEqual, useSelector } from 'react-redux'; import deepEqual from 'fast-deep-equal'; import { ESTermQuery } from '../../../../common/typed_json'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel, State } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; import { createFilter } from '../../../common/containers/helpers'; @@ -46,6 +45,7 @@ export interface NetworkTlsArgs { interface UseNetworkTls { flowTarget: FlowTargetSourceDest; + indexNames: string[]; ip: string; type: networkModel.NetworkType; filterQuery?: ESTermQuery | string; @@ -60,6 +60,7 @@ export const useNetworkTls = ({ filterQuery, flowTarget, id = ID, + indexNames, ip, skip, startDate, @@ -70,14 +71,13 @@ export const useNetworkTls = ({ (state: State) => getTlsSelector(state, type, flowTarget), shallowEqual ); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [networkTlsRequest, setHostRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: NetworkQueries.tls, filterQuery: createFilter(filterQuery), flowTarget, @@ -178,7 +178,7 @@ export const useNetworkTls = ({ setHostRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), pagination: generateTablePaginationOptions(activePage, limit), timerange: { @@ -193,7 +193,7 @@ export const useNetworkTls = ({ } return prevRequest; }); - }, [activePage, defaultIndex, endDate, filterQuery, limit, startDate, sort, skip]); + }, [activePage, indexNames, endDate, filterQuery, limit, startDate, sort, skip]); useEffect(() => { networkTlsSearch(networkTlsRequest); diff --git a/x-pack/plugins/security_solution/public/network/pages/details/index.test.tsx b/x-pack/plugins/security_solution/public/network/pages/details/index.test.tsx index beae3e4f72aaa5..430b5702be1bc2 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/index.test.tsx @@ -9,7 +9,7 @@ import { Router, useParams } from 'react-router-dom'; import '../../../common/mock/match_media'; -import { useWithSource } from '../../../common/containers/source'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; import { FlowTarget } from '../../../graphql/types'; import { apolloClientObservable, @@ -40,7 +40,7 @@ jest.mock('../../containers/details', () => ({ useNetworkDetails: jest.fn().mockReturnValue([true, { networkDetails: {} }]), })); jest.mock('../../../common/lib/kibana'); -jest.mock('../../../common/containers/source'); +jest.mock('../../../common/containers/sourcerer'); jest.mock('../../../common/containers/use_global_time', () => ({ useGlobalTime: jest.fn().mockReturnValue({ from: '2020-07-07T08:20:18.966Z', @@ -81,7 +81,7 @@ const getMockHistory = (ip: string) => ({ describe('Network Details', () => { const mount = useMountAppended(); beforeAll(() => { - (useWithSource as jest.Mock).mockReturnValue({ + (useSourcererScope as jest.Mock).mockReturnValue({ indicesExist: false, indexPattern: {}, }); @@ -137,7 +137,7 @@ describe('Network Details', () => { test('it renders ipv6 headline', async () => { const ip = 'fe80--24ce-f7ff-fede-a571'; - (useWithSource as jest.Mock).mockReturnValue({ + (useSourcererScope as jest.Mock).mockReturnValue({ indicesExist: true, indexPattern: {}, }); diff --git a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx index 085cddf53ff65e..eaeb31c020473c 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx @@ -24,7 +24,6 @@ import { IpOverview } from '../../components/details'; import { SiemSearchBar } from '../../../common/components/search_bar'; import { WrapperPage } from '../../../common/components/wrapper_page'; import { useNetworkDetails } from '../../containers/details'; -import { useWithSource } from '../../../common/containers/source'; import { FlowTargetSourceDest, LastEventIndexKey } from '../../../graphql/types'; import { useKibana } from '../../../common/lib/kibana'; import { decodeIpv6 } from '../../../common/lib/helpers'; @@ -44,6 +43,7 @@ import { AnomaliesQueryTabBody } from '../../../common/containers/anomalies/anom import { esQuery } from '../../../../../../../src/plugins/data/public'; import { networkModel } from '../../store'; import { SecurityPageName } from '../../../app/types'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; export { getBreadcrumbs } from './utils'; const NetworkDetailsManage = manageQuery(IpOverview); @@ -83,7 +83,7 @@ const NetworkDetailsComponent: React.FC = () => { dispatch(setNetworkDetailsTablesActivePageToZero()); }, [detailName, dispatch]); - const { docValueFields, indicesExist, indexPattern } = useWithSource(); + const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererScope(); const ip = decodeIpv6(detailName); const filterQuery = convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(uiSettings), @@ -96,6 +96,7 @@ const NetworkDetailsComponent: React.FC = () => { docValueFields, skip: isInitializing, filterQuery, + indexNames: selectedPatterns, ip, }); @@ -124,7 +125,14 @@ const NetworkDetailsComponent: React.FC = () => { border data-test-subj="network-details-headline" draggableArguments={headerDraggableArguments} - subtitle={} + subtitle={ + + } title={ip} > @@ -155,6 +163,7 @@ const NetworkDetailsComponent: React.FC = () => { endDate={to} filterQuery={filterQuery} flowTarget={FlowTargetSourceDest.source} + indexNames={selectedPatterns} ip={ip} skip={isInitializing} startDate={from} @@ -169,6 +178,7 @@ const NetworkDetailsComponent: React.FC = () => { endDate={to} flowTarget={FlowTargetSourceDest.destination} filterQuery={filterQuery} + indexNames={selectedPatterns} ip={ip} skip={isInitializing} startDate={from} @@ -187,6 +197,7 @@ const NetworkDetailsComponent: React.FC = () => { endDate={to} filterQuery={filterQuery} flowTarget={FlowTargetSourceDest.source} + indexNames={selectedPatterns} ip={ip} skip={isInitializing} startDate={from} @@ -201,6 +212,7 @@ const NetworkDetailsComponent: React.FC = () => { endDate={to} flowTarget={FlowTargetSourceDest.destination} filterQuery={filterQuery} + indexNames={selectedPatterns} ip={ip} skip={isInitializing} startDate={from} @@ -217,6 +229,7 @@ const NetworkDetailsComponent: React.FC = () => { endDate={to} filterQuery={filterQuery} flowTarget={flowTarget} + indexNames={selectedPatterns} ip={ip} skip={isInitializing} startDate={from} @@ -229,6 +242,7 @@ const NetworkDetailsComponent: React.FC = () => { { endDate={to} filterQuery={filterQuery} flowTarget={(flowTarget as unknown) as FlowTargetSourceDest} + indexNames={selectedPatterns} ip={ip} setQuery={setQuery} skip={isInitializing} @@ -257,6 +272,7 @@ const NetworkDetailsComponent: React.FC = () => { startDate={from} endDate={to} skip={isInitializing} + indexNames={selectedPatterns} ip={ip} type={type} flowTarget={flowTarget} diff --git a/x-pack/plugins/security_solution/public/network/pages/details/network_http_query_table.tsx b/x-pack/plugins/security_solution/public/network/pages/details/network_http_query_table.tsx index 1b1b2b5f4f46e2..0a885193904863 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/network_http_query_table.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/network_http_query_table.tsx @@ -28,6 +28,7 @@ export const NetworkHttpQueryTable = ({ ] = useNetworkHttp({ endDate, filterQuery, + indexNames: [], ip, skip, startDate, diff --git a/x-pack/plugins/security_solution/public/network/pages/details/network_top_countries_query_table.tsx b/x-pack/plugins/security_solution/public/network/pages/details/network_top_countries_query_table.tsx index 42ddd3a6bb4a43..8a7d499a8ef5fa 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/network_top_countries_query_table.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/network_top_countries_query_table.tsx @@ -31,6 +31,7 @@ export const NetworkTopCountriesQueryTable = ({ endDate, flowTarget, filterQuery, + indexNames: [], ip, skip, startDate, diff --git a/x-pack/plugins/security_solution/public/network/pages/details/network_top_n_flow_query_table.tsx b/x-pack/plugins/security_solution/public/network/pages/details/network_top_n_flow_query_table.tsx index 821452201b78b0..d56d6d4f6b3ee5 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/network_top_n_flow_query_table.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/network_top_n_flow_query_table.tsx @@ -18,6 +18,7 @@ export const NetworkTopNFlowQueryTable = ({ filterQuery, flowTarget, ip, + indexNames, setQuery, skip, startDate, @@ -30,6 +31,7 @@ export const NetworkTopNFlowQueryTable = ({ endDate, filterQuery, flowTarget, + indexNames, ip, skip, startDate, diff --git a/x-pack/plugins/security_solution/public/network/pages/details/tls_query_table.tsx b/x-pack/plugins/security_solution/public/network/pages/details/tls_query_table.tsx index 5184fccecf07a5..b8c53cdf10fee8 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/tls_query_table.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/tls_query_table.tsx @@ -30,6 +30,7 @@ export const TlsQueryTable = ({ endDate, filterQuery, flowTarget, + indexNames: [], ip, skip, startDate, diff --git a/x-pack/plugins/security_solution/public/network/pages/details/types.ts b/x-pack/plugins/security_solution/public/network/pages/details/types.ts index 960df0d5e36b9b..3b5a7fab3c6e73 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/types.ts +++ b/x-pack/plugins/security_solution/public/network/pages/details/types.ts @@ -22,6 +22,7 @@ export interface OwnProps { endDate: string; filterQuery: string | ESTermQuery; ip: string; + indexNames: string[]; skip: boolean; setQuery: GlobalTimeArgs['setQuery']; } diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/countries_query_tab_body.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/countries_query_tab_body.tsx index 1e57ca42257e78..1c61760d9845f0 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/countries_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/countries_query_tab_body.tsx @@ -19,6 +19,7 @@ const NetworkTopCountriesTableManage = manageQuery(NetworkTopCountriesTable); export const CountriesQueryTabBody = ({ endDate, filterQuery, + indexNames, skip, startDate, setQuery, @@ -32,6 +33,7 @@ export const CountriesQueryTabBody = ({ endDate, flowTarget, filterQuery, + indexNames, skip, startDate, type: networkModel.NetworkType.page, diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/dns_query_tab_body.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/dns_query_tab_body.tsx index 5adb78edbec8e5..a8bae2509e0d6e 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/dns_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/dns_query_tab_body.tsx @@ -45,6 +45,7 @@ const DnsQueryTabBodyComponent: React.FC = ({ deleteQuery, endDate, filterQuery, + indexNames, skip, startDate, setQuery, @@ -64,6 +65,7 @@ const DnsQueryTabBodyComponent: React.FC = ({ ] = useNetworkDns({ endDate, filterQuery, + indexNames, skip, startDate, type, @@ -88,6 +90,7 @@ const DnsQueryTabBodyComponent: React.FC = ({ endDate={endDate} filterQuery={filterQuery} id={HISTOGRAM_ID} + indexNames={indexNames} setQuery={setQuery} showLegend={true} startDate={startDate} diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/http_query_tab_body.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/http_query_tab_body.tsx index 3caff05734c1e4..85d6b6daabd6c4 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/http_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/http_query_tab_body.tsx @@ -19,6 +19,7 @@ const NetworkHttpTableManage = manageQuery(NetworkHttpTable); export const HttpQueryTabBody = ({ endDate, filterQuery, + indexNames, skip, startDate, setQuery, @@ -29,6 +30,7 @@ export const HttpQueryTabBody = ({ ] = useNetworkHttp({ endDate, filterQuery, + indexNames, skip, startDate, type: networkModel.NetworkType.page, diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/ips_query_tab_body.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/ips_query_tab_body.tsx index c83bf6ff809012..465b3347ce707f 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/ips_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/ips_query_tab_body.tsx @@ -19,6 +19,7 @@ const NetworkTopNFlowTableManage = manageQuery(NetworkTopNFlowTable); export const IPsQueryTabBody = ({ endDate, filterQuery, + indexNames, skip, startDate, setQuery, @@ -31,6 +32,7 @@ export const IPsQueryTabBody = ({ endDate, flowTarget, filterQuery, + indexNames, skip, startDate, type: networkModel.NetworkType.page, diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/network_routes.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/network_routes.tsx index 2da56a30df7c75..7af474728c824b 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/network_routes.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/network_routes.tsx @@ -33,6 +33,7 @@ export const NetworkRoutes = React.memo( isInitializing, from, indexPattern, + indexNames, setQuery, setAbsoluteRangeDatePicker, }) => { @@ -83,6 +84,7 @@ export const NetworkRoutes = React.memo( const commonProps = { startDate: from, endDate: to, + indexNames, skip: isInitializing, type, narrowDateRange, diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/tls_query_tab_body.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/tls_query_tab_body.tsx index 279891cc181e3d..702a9e696220f7 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/tls_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/tls_query_tab_body.tsx @@ -16,6 +16,7 @@ const TlsQueryTabBodyComponent: React.FC = ({ endDate, filterQuery, flowTarget, + indexNames, ip = '', setQuery, skip, @@ -29,6 +30,7 @@ const TlsQueryTabBodyComponent: React.FC = ({ endDate, filterQuery, flowTarget, + indexNames, ip, skip, startDate, diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts b/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts index 2ef04d3371c0b9..ed04fd01a7b891 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts @@ -22,6 +22,7 @@ interface QueryTabBodyProps extends Pick ({ +const mockProps = { networkPagePath: '', to, from, @@ -69,40 +69,42 @@ const getMockProps = () => ({ setQuery: jest.fn(), capabilitiesFetched: true, hasMlUserPermissions: true, -}); - +}; +const mockUseSourcererScope = useSourcererScope as jest.Mock; describe('rendering - rendering', () => { - test('it renders the Setup Instructions text when no index is available', async () => { - (useWithSource as jest.Mock).mockReturnValue({ + test('it renders the Setup Instructions text when no index is available', () => { + mockUseSourcererScope.mockReturnValue({ + selectedPatterns: [], indicesExist: false, }); const wrapper = mount( - + ); expect(wrapper.find('[data-test-subj="empty-page"]').exists()).toBe(true); }); - test('it DOES NOT render the Setup Instructions text when an index is available', async () => { - (useWithSource as jest.Mock).mockReturnValue({ + test('it DOES NOT render the Setup Instructions text when an index is available', () => { + mockUseSourcererScope.mockReturnValue({ + selectedPatterns: [], indicesExist: true, indexPattern: {}, }); const wrapper = mount( - + ); expect(wrapper.find('[data-test-subj="empty-page"]').exists()).toBe(false); }); - test('it should add the new filters after init', async () => { + test('it should add the new filters after init', () => { const newFilters: Filter[] = [ { query: { @@ -134,7 +136,8 @@ describe('rendering - rendering', () => { }, }, ]; - (useWithSource as jest.Mock).mockReturnValue({ + mockUseSourcererScope.mockReturnValue({ + selectedPatterns: [], indicesExist: true, indexPattern: { fields: [], title: 'title' }, }); @@ -150,7 +153,7 @@ describe('rendering - rendering', () => { const wrapper = mount( - + ); diff --git a/x-pack/plugins/security_solution/public/network/pages/network.tsx b/x-pack/plugins/security_solution/public/network/pages/network.tsx index 8aed6385ea24d6..6aea771e49499f 100644 --- a/x-pack/plugins/security_solution/public/network/pages/network.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/network.tsx @@ -24,7 +24,6 @@ import { SiemSearchBar } from '../../common/components/search_bar'; import { WrapperPage } from '../../common/components/wrapper_page'; import { useFullScreen } from '../../common/containers/use_full_screen'; import { useGlobalTime } from '../../common/containers/use_global_time'; -import { useWithSource } from '../../common/containers/source'; import { LastEventIndexKey } from '../../graphql/types'; import { useKibana } from '../../common/lib/kibana'; import { convertToBuildEsQuery } from '../../common/lib/keury'; @@ -44,8 +43,7 @@ import { timelineSelectors } from '../../timelines/store/timeline'; import { TimelineId } from '../../../common/types/timeline'; import { timelineDefaults } from '../../timelines/store/timeline/defaults'; import { TimelineModel } from '../../timelines/store/timeline/model'; - -const sourceId = 'default'; +import { useSourcererScope } from '../../common/containers/sourcerer'; const NetworkComponent = React.memo( ({ @@ -84,7 +82,7 @@ const NetworkComponent = React.memo( [setAbsoluteRangeDatePicker] ); - const { indicesExist, indexPattern } = useWithSource(sourceId); + const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererScope(); const filterQuery = convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(kibana.services.uiSettings), indexPattern, @@ -111,7 +109,13 @@ const NetworkComponent = React.memo( } + subtitle={ + + } title={i18n.PAGE_TITLE} /> @@ -127,11 +131,12 @@ const NetworkComponent = React.memo( @@ -150,6 +155,7 @@ const NetworkComponent = React.memo( from={from} isInitializing={isInitializing} indexPattern={indexPattern} + indexNames={selectedPatterns} setQuery={setQuery} setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker} type={networkModel.NetworkType.page} diff --git a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.test.tsx index e365ac38d31df3..6f1b7e95e763dc 100644 --- a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.test.tsx @@ -29,7 +29,15 @@ const to = '2019-03-31T06:00:00.000Z'; describe('Alerts by category', () => { let wrapper: ReactWrapper; - + const testProps = { + deleteQuery: jest.fn(), + filters: [], + from, + indexNames: [], + indexPattern: mockIndexPattern, + setQuery: jest.fn(), + to, + }; describe('before loading data', () => { beforeAll(async () => { (useMatrixHistogram as jest.Mock).mockReturnValue([ @@ -44,14 +52,7 @@ describe('Alerts by category', () => { wrapper = mount( - + ); @@ -119,14 +120,7 @@ describe('Alerts by category', () => { wrapper = mount( - + ); diff --git a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx index 1a2238c763bdad..4d3b2dbf3f11f6 100644 --- a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx @@ -43,6 +43,7 @@ interface Props extends Pick = ({ from, hideHeaderChildren = false, indexPattern, + indexNames, query = DEFAULT_QUERY, setQuery, to, @@ -117,6 +119,7 @@ const AlertsByCategoryComponent: React.FC = ({ })} headerChildren={hideHeaderChildren ? null : alertsCountViewAlertsButton} id={ID} + indexNames={indexNames} setQuery={setQuery} startDate={from} {...alertsByCategoryHistogramConfigs} diff --git a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.test.tsx index f2d6b503260825..44cb7a65dbc5e9 100644 --- a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.test.tsx @@ -20,11 +20,16 @@ describe('EventCounts', () => { const from = '2020-01-20T20:49:57.080Z'; const to = '2020-01-21T20:49:57.080Z'; + const testProps = { + from, + indexNames: [], + indexPattern: mockIndexPattern, + setQuery: jest.fn(), + to, + }; + test('it filters the `Host events` widget with a `host.name` `exists` filter', () => { - const wrapper = mount( - , - { wrappingComponent: TestProviders } - ); + const wrapper = mount(, { wrappingComponent: TestProviders }); expect( (wrapper.find('Memo(OverviewHostComponent)').first().props() as OverviewHostProps).filterQuery @@ -32,10 +37,7 @@ describe('EventCounts', () => { }); test('it filters the `Network events` widget with a `source.ip` or `destination.ip` `exists` filter', () => { - const wrapper = mount( - , - { wrappingComponent: TestProviders } - ); + const wrapper = mount(, { wrappingComponent: TestProviders }); expect( (wrapper.find('Memo(OverviewNetworkComponent)').first().props() as OverviewNetworkProps) diff --git a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx index 23f5998f44111e..6e47de68221c79 100644 --- a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx @@ -31,6 +31,7 @@ const DEFAULT_QUERY: Query = { query: '', language: 'kuery' }; interface Props extends Pick { filters?: Filter[]; + indexNames: string[]; indexPattern: IIndexPattern; query?: Query; } @@ -38,6 +39,7 @@ interface Props extends Pick { const EventCountsComponent: React.FC = ({ filters = NO_FILTERS, from, + indexNames, indexPattern, query = DEFAULT_QUERY, setQuery, @@ -56,6 +58,7 @@ const EventCountsComponent: React.FC = ({ queries: [query], filters: [...filters, ...filterHostData], })} + indexNames={indexNames} startDate={from} setQuery={setQuery} /> @@ -72,6 +75,7 @@ const EventCountsComponent: React.FC = ({ queries: [query], filters: [...filters, ...filterNetworkData], })} + indexNames={indexNames} startDate={from} setQuery={setQuery} /> diff --git a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx index 7025afde963f14..1fa3d8f4ef27aa 100644 --- a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx @@ -47,7 +47,7 @@ interface Props extends Pick = ({ from, headerChildren, indexPattern, - indexToAdd, + indexNames, onlyField, query = DEFAULT_QUERY, setAbsoluteRangeDatePickerTarget, @@ -164,7 +164,7 @@ const EventsByDatasetComponent: React.FC = ({ filterQuery={filterQuery} headerChildren={headerContent} id={uniqueQueryId} - indexToAdd={indexToAdd} + indexNames={indexNames} setAbsoluteRangeDatePickerTarget={setAbsoluteRangeDatePickerTarget} setQuery={setQuery} showSpacer={showSpacer} diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/overview/components/host_overview/__snapshots__/index.test.tsx.snap index c9c34682519e28..47d45ab740dcf2 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/__snapshots__/index.test.tsx.snap @@ -192,8 +192,10 @@ exports[`Host Summary Component rendering it renders the default Host Summary 1` }, } } + docValueFields={Array []} endDate="2019-06-18T06:00:00.000Z" id="hostOverview" + indexNames={Array []} isLoadingAnomaliesData={false} loading={false} narrowDateRange={[MockFunction]} diff --git a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx index 6bd0390d014a37..69bd053d707b92 100644 --- a/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/host_overview/index.test.tsx @@ -20,8 +20,10 @@ describe('Host Summary Component', () => { ( ({ anomaliesData, data, + docValueFields, endDate, id, isLoadingAnomaliesData, + indexNames, loading, narrowDateRange, startDate, @@ -91,7 +95,9 @@ export const HostOverview = React.memo( description: data.host != null && data.host.name && data.host.name.length ? ( ) : ( @@ -103,7 +109,9 @@ export const HostOverview = React.memo( description: data.host != null && data.host.name && data.host.name.length ? ( ) : ( @@ -111,7 +119,7 @@ export const HostOverview = React.memo( ), }, ], - [data] + [data, docValueFields, indexNames] ); const firstColumn = useMemo( () => diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx index b932add7afc2c7..8f0e9a56254ec6 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx @@ -27,7 +27,12 @@ jest.mock('../../../common/components/link_to'); const startDate = '2020-01-20T20:49:57.080Z'; const endDate = '2020-01-21T20:49:57.080Z'; - +const testProps = { + endDate, + indexNames: [], + setQuery: jest.fn(), + startDate, +}; const MOCKED_RESPONSE = { overviewHost: { auditbeatAuditd: 1, @@ -79,7 +84,7 @@ describe('OverviewHost', () => { test('it renders the expected widget title', () => { const wrapper = mount( - + ); @@ -92,7 +97,7 @@ describe('OverviewHost', () => { useHostOverviewMock.mockReturnValueOnce([true, { overviewHost: {} }]); const wrapper = mount( - + ); @@ -102,7 +107,7 @@ describe('OverviewHost', () => { test('it renders the expected event count in the subtitle after loading events', async () => { const wrapper = mount( - + ); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx index 3f35d0abbaa856..f92f004bd2448b 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx @@ -22,11 +22,11 @@ import { InspectButtonContainer } from '../../../common/components/inspect'; import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; import { SecurityPageName } from '../../../app/types'; import { LinkButton } from '../../../common/components/links'; -import { Sourcerer } from '../../../common/components/sourcerer'; export interface OwnProps { startDate: GlobalTimeArgs['from']; endDate: GlobalTimeArgs['to']; + indexNames: string[]; filterQuery?: ESQuery | string; setQuery: GlobalTimeArgs['setQuery']; } @@ -37,6 +37,7 @@ export type OverviewHostProps = OwnProps; const OverviewHostComponent: React.FC = ({ endDate, filterQuery, + indexNames, startDate, setQuery, }) => { @@ -47,6 +48,7 @@ const OverviewHostComponent: React.FC = ({ const [loading, { overviewHost, id, inspect, refetch }] = useHostOverview({ endDate, filterQuery, + indexNames, startDate, }); @@ -109,10 +111,7 @@ const OverviewHostComponent: React.FC = ({ /> } > - <> - - {hostPageButton} - + <>{hostPageButton} { const startDate = '2020-01-20T20:49:57.080Z'; const endDate = '2020-01-21T20:49:57.080Z'; +const defaultProps = { + endDate, + startDate, + setQuery: jest.fn(), + indexNames: [], +}; const MOCKED_RESPONSE = { overviewNetwork: { @@ -88,7 +94,7 @@ describe('OverviewNetwork', () => { test('it renders the expected widget title', () => { const wrapper = mount( - + ); @@ -101,7 +107,7 @@ describe('OverviewNetwork', () => { useNetworkOverviewMock.mockReturnValueOnce([true, { overviewNetwork: {} }]); const wrapper = mount( - + ); @@ -111,7 +117,7 @@ describe('OverviewNetwork', () => { test('it renders the expected event count in the subtitle after loading events', async () => { const wrapper = mount( - + ); @@ -123,7 +129,7 @@ describe('OverviewNetwork', () => { it('it renders View Network', () => { const wrapper = mount( - + ); @@ -133,7 +139,7 @@ describe('OverviewNetwork', () => { it('when click on View Network we call navigateToApp to make sure to navigate to right page', () => { const wrapper = mount( - + ); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx index 089bed3c678087..178a752d1286f1 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx @@ -30,6 +30,7 @@ export interface OverviewNetworkProps { startDate: GlobalTimeArgs['from']; endDate: GlobalTimeArgs['to']; filterQuery?: ESQuery | string; + indexNames: string[]; setQuery: GlobalTimeArgs['setQuery']; } @@ -38,6 +39,7 @@ const OverviewNetworkStatsManage = manageQuery(OverviewNetworkStats); const OverviewNetworkComponent: React.FC = ({ endDate, filterQuery, + indexNames, startDate, setQuery, }) => { @@ -48,6 +50,7 @@ const OverviewNetworkComponent: React.FC = ({ const [loading, { overviewNetwork, id, inspect, refetch }] = useNetworkOverview({ endDate, filterQuery, + indexNames, startDate, }); diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_cases/no_cases/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_cases/no_cases/index.test.tsx index 446679ae26d9ec..8a0d2f4f162020 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_cases/no_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_cases/no_cases/index.test.tsx @@ -33,7 +33,7 @@ describe('RecentCases', () => { wrapper.find(`[data-test-subj="no-cases-create-case"]`).first().simulate('click'); expect(navigateToApp).toHaveBeenCalledWith('securitySolution:case', { path: - "/create?timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))", + "/create?sourcerer=(default:!('apm-*-transaction*','auditbeat-*','endgame-*','filebeat-*','logs-*','packetbeat-*','winlogbeat-*'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))", }); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.tsx b/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.tsx index 75ab85fe0c4297..ac439107cb4a5c 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.tsx @@ -5,10 +5,9 @@ */ import { noop } from 'lodash/fp'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { HostsQueries, HostOverviewRequestOptions, @@ -18,8 +17,6 @@ import { useKibana } from '../../../common/lib/kibana'; import { inputsModel } from '../../../common/store/inputs'; import { createFilter } from '../../../common/containers/helpers'; import { ESQuery } from '../../../../common/typed_json'; -import { useManageSource } from '../../../common/containers/sourcerer'; -import { SOURCERER_FEATURE_FLAG_ON } from '../../../common/containers/sourcerer/constants'; import { AbortError, isCompleteResponse, @@ -42,6 +39,7 @@ export interface HostOverviewArgs { interface UseHostOverview { filterQuery?: ESQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; } @@ -49,23 +47,16 @@ interface UseHostOverview { export const useHostOverview = ({ filterQuery, endDate, + indexNames, skip = false, startDate, }: UseHostOverview): [boolean, HostOverviewArgs] => { - const { data, notifications, uiSettings } = useKibana().services; - const { activeSourceGroupId, getManageSourceGroupById } = useManageSource(); - const { indexPatterns } = useMemo(() => getManageSourceGroupById(activeSourceGroupId), [ - getManageSourceGroupById, - activeSourceGroupId, - ]); - const uiDefaultIndexPatterns = uiSettings.get(DEFAULT_INDEX_KEY); - const defaultIndex = SOURCERER_FEATURE_FLAG_ON ? indexPatterns : uiDefaultIndexPatterns; - + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); const [loading, setLoading] = useState(false); const [overviewHostRequest, setHostRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: HostsQueries.overview, filterQuery: createFilter(filterQuery), timerange: { @@ -145,7 +136,7 @@ export const useHostOverview = ({ setHostRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), timerange: { interval: '12h', @@ -158,7 +149,7 @@ export const useHostOverview = ({ } return prevRequest; }); - }, [defaultIndex, endDate, filterQuery, skip, startDate]); + }, [indexNames, endDate, filterQuery, skip, startDate]); useEffect(() => { overviewHostSearch(overviewHostRequest); diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.tsx b/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.tsx index ae1fe942d8403a..588fb1f08ef6f8 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.tsx @@ -8,7 +8,6 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { NetworkQueries, NetworkOverviewRequestOptions, @@ -40,6 +39,7 @@ export interface NetworkOverviewArgs { interface UseNetworkOverview { filterQuery?: ESQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; } @@ -47,16 +47,16 @@ interface UseNetworkOverview { export const useNetworkOverview = ({ filterQuery, endDate, + indexNames, skip = false, startDate, }: UseNetworkOverview): [boolean, NetworkOverviewArgs] => { - const { data, notifications, uiSettings } = useKibana().services; - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); const [loading, setLoading] = useState(false); const [overviewNetworkRequest, setNetworkRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: NetworkQueries.overview, filterQuery: createFilter(filterQuery), timerange: { @@ -136,7 +136,7 @@ export const useNetworkOverview = ({ setNetworkRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), timerange: { interval: '12h', @@ -149,7 +149,7 @@ export const useNetworkOverview = ({ } return prevRequest; }); - }, [defaultIndex, endDate, filterQuery, skip, startDate]); + }, [indexNames, endDate, filterQuery, skip, startDate]); useEffect(() => { overviewNetworkSearch(overviewNetworkRequest); diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx index 74225c4e4f8232..222b9e008ddd77 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx @@ -10,16 +10,18 @@ import { MemoryRouter } from 'react-router-dom'; import '../../common/mock/match_media'; import { TestProviders } from '../../common/mock'; -import { useWithSource } from '../../common/containers/source'; import { useMessagesStorage, UseMessagesStorage, } from '../../common/containers/local_storage/use_messages_storage'; import { Overview } from './index'; import { useIngestEnabledCheck } from '../../common/hooks/endpoint/ingest_enabled'; +import { useSourcererScope } from '../../common/containers/sourcerer'; +import { useFetchIndex } from '../../common/containers/source'; jest.mock('../../common/lib/kibana'); jest.mock('../../common/containers/source'); +jest.mock('../../common/containers/sourcerer'); jest.mock('../../common/containers/use_global_time', () => ({ useGlobalTime: jest.fn().mockReturnValue({ from: '2020-07-07T08:20:18.966Z', @@ -49,65 +51,29 @@ const endpointNoticeMessage = (hasMessageValue: boolean) => { clearAllMessages: () => undefined, }; }; - +const mockUseSourcererScope = useSourcererScope as jest.Mock; +const mockUseIngestEnabledCheck = useIngestEnabledCheck as jest.Mock; +const mockUseFetchIndex = useFetchIndex as jest.Mock; +const mockUseMessagesStorage: jest.Mock = useMessagesStorage as jest.Mock; describe('Overview', () => { + beforeEach(() => { + mockUseFetchIndex.mockReturnValue([ + false, + { + indexExists: true, + }, + ]); + }); describe('rendering', () => { - describe('when no index is available', () => { - beforeEach(() => { - (useWithSource as jest.Mock).mockReturnValue({ - indicesExist: false, - }); - (useIngestEnabledCheck as jest.Mock).mockReturnValue({ allEnabled: false }); - const mockuseMessagesStorage: jest.Mock = useMessagesStorage as jest.Mock< - UseMessagesStorage - >; - mockuseMessagesStorage.mockImplementation(() => endpointNoticeMessage(false)); - }); - - it('renders the Setup Instructions text', () => { - const wrapper = mount( - - - - - - ); - expect(wrapper.find('[data-test-subj="empty-page"]').exists()).toBe(true); - }); - - it('does not show Endpoint get ready button when ingest is not enabled', () => { - const wrapper = mount( - - - - - - ); - expect(wrapper.find('[data-test-subj="empty-page-endpoint-action"]').exists()).toBe(false); - }); - - it('shows Endpoint get ready button when ingest is enabled', () => { - (useIngestEnabledCheck as jest.Mock).mockReturnValue({ allEnabled: true }); - const wrapper = mount( - - - - - - ); - expect(wrapper.find('[data-test-subj="empty-page-endpoint-action"]').exists()).toBe(true); - }); - }); - - it('it DOES NOT render the Getting started text when an index is available', () => { - (useWithSource as jest.Mock).mockReturnValue({ + test('it DOES NOT render the Getting started text when an index is available', () => { + mockUseSourcererScope.mockReturnValue({ + selectedPatterns: [], indicesExist: true, indexPattern: {}, }); - const mockuseMessagesStorage: jest.Mock = useMessagesStorage as jest.Mock; - mockuseMessagesStorage.mockImplementation(() => endpointNoticeMessage(false)); - (useIngestEnabledCheck as jest.Mock).mockReturnValue({ allEnabled: true }); + mockUseMessagesStorage.mockImplementation(() => endpointNoticeMessage(false)); + mockUseIngestEnabledCheck.mockReturnValue({ allEnabled: true }); const wrapper = mount( @@ -122,19 +88,20 @@ describe('Overview', () => { }); test('it DOES render the Endpoint banner when the endpoint index is NOT available AND storage is NOT set', () => { - (useWithSource as jest.Mock).mockReturnValueOnce({ + mockUseFetchIndex.mockReturnValue([ + false, + { + indexExists: false, + }, + ]); + mockUseSourcererScope.mockReturnValue({ + selectedPatterns: [], indicesExist: true, indexPattern: {}, }); - (useWithSource as jest.Mock).mockReturnValueOnce({ - indicesExist: false, - indexPattern: {}, - }); - - const mockuseMessagesStorage: jest.Mock = useMessagesStorage as jest.Mock; - mockuseMessagesStorage.mockImplementation(() => endpointNoticeMessage(false)); - (useIngestEnabledCheck as jest.Mock).mockReturnValue({ allEnabled: true }); + mockUseMessagesStorage.mockImplementation(() => endpointNoticeMessage(false)); + mockUseIngestEnabledCheck.mockReturnValue({ allEnabled: true }); const wrapper = mount( @@ -149,19 +116,20 @@ describe('Overview', () => { }); test('it does NOT render the Endpoint banner when the endpoint index is NOT available but storage is set', () => { - (useWithSource as jest.Mock).mockReturnValueOnce({ + mockUseFetchIndex.mockReturnValue([ + false, + { + indexExists: false, + }, + ]); + mockUseSourcererScope.mockReturnValueOnce({ + selectedPatterns: [], indicesExist: true, indexPattern: {}, }); - (useWithSource as jest.Mock).mockReturnValueOnce({ - indicesExist: false, - indexPattern: {}, - }); - - const mockuseMessagesStorage: jest.Mock = useMessagesStorage as jest.Mock; - mockuseMessagesStorage.mockImplementation(() => endpointNoticeMessage(true)); - (useIngestEnabledCheck as jest.Mock).mockReturnValue({ allEnabled: true }); + mockUseMessagesStorage.mockImplementation(() => endpointNoticeMessage(true)); + mockUseIngestEnabledCheck.mockReturnValue({ allEnabled: true }); const wrapper = mount( @@ -176,14 +144,14 @@ describe('Overview', () => { }); test('it does NOT render the Endpoint banner when the endpoint index is available AND storage is set', () => { - (useWithSource as jest.Mock).mockReturnValue({ - indicesExist: true, + mockUseSourcererScope.mockReturnValue({ + selectedPatterns: [], + indexExists: true, indexPattern: {}, }); - const mockuseMessagesStorage: jest.Mock = useMessagesStorage as jest.Mock; - mockuseMessagesStorage.mockImplementation(() => endpointNoticeMessage(true)); - (useIngestEnabledCheck as jest.Mock).mockReturnValue({ allEnabled: true }); + mockUseMessagesStorage.mockImplementation(() => endpointNoticeMessage(true)); + mockUseIngestEnabledCheck.mockReturnValue({ allEnabled: true }); const wrapper = mount( @@ -198,14 +166,14 @@ describe('Overview', () => { }); test('it does NOT render the Endpoint banner when an index IS available but storage is NOT set', () => { - (useWithSource as jest.Mock).mockReturnValue({ + mockUseSourcererScope.mockReturnValue({ + selectedPatterns: [], indicesExist: true, indexPattern: {}, }); - const mockuseMessagesStorage: jest.Mock = useMessagesStorage as jest.Mock; - mockuseMessagesStorage.mockImplementation(() => endpointNoticeMessage(false)); - (useIngestEnabledCheck as jest.Mock).mockReturnValue({ allEnabled: true }); + mockUseMessagesStorage.mockImplementation(() => endpointNoticeMessage(false)); + mockUseIngestEnabledCheck.mockReturnValue({ allEnabled: true }); const wrapper = mount( @@ -214,19 +182,20 @@ describe('Overview', () => { ); + wrapper.update(); expect(wrapper.find('[data-test-subj="endpoint-prompt-banner"]').exists()).toBe(false); wrapper.unmount(); }); test('it does NOT render the Endpoint banner when Ingest is NOT available', () => { - (useWithSource as jest.Mock).mockReturnValue({ + mockUseSourcererScope.mockReturnValue({ + selectedPatterns: [], indicesExist: true, indexPattern: {}, }); - const mockuseMessagesStorage: jest.Mock = useMessagesStorage as jest.Mock; - mockuseMessagesStorage.mockImplementation(() => endpointNoticeMessage(true)); - (useIngestEnabledCheck as jest.Mock).mockReturnValue({ allEnabled: false }); + mockUseMessagesStorage.mockImplementation(() => endpointNoticeMessage(true)); + mockUseIngestEnabledCheck.mockReturnValue({ allEnabled: false }); const wrapper = mount( @@ -239,5 +208,50 @@ describe('Overview', () => { expect(wrapper.find('[data-test-subj="endpoint-prompt-banner"]').exists()).toBe(false); wrapper.unmount(); }); + + describe('when no index is available', () => { + beforeEach(() => { + mockUseSourcererScope.mockReturnValue({ + selectedPatterns: [], + indicesExist: false, + }); + mockUseIngestEnabledCheck.mockReturnValue({ allEnabled: false }); + mockUseMessagesStorage.mockImplementation(() => endpointNoticeMessage(false)); + }); + + it('renders the Setup Instructions text', () => { + const wrapper = mount( + + + + + + ); + expect(wrapper.find('[data-test-subj="empty-page"]').exists()).toBe(true); + }); + + it('does not show Endpoint get ready button when ingest is not enabled', () => { + const wrapper = mount( + + + + + + ); + expect(wrapper.find('[data-test-subj="empty-page-endpoint-action"]').exists()).toBe(false); + }); + + it('shows Endpoint get ready button when ingest is enabled', () => { + mockUseIngestEnabledCheck.mockReturnValue({ allEnabled: true }); + const wrapper = mount( + + + + + + ); + expect(wrapper.find('[data-test-subj="empty-page-endpoint-action"]').exists()).toBe(true); + }); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx index 520fd6c4597055..5a3b4ec3846869 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx @@ -15,7 +15,7 @@ import { FiltersGlobal } from '../../common/components/filters_global'; import { SiemSearchBar } from '../../common/components/search_bar'; import { WrapperPage } from '../../common/components/wrapper_page'; import { useGlobalTime } from '../../common/containers/use_global_time'; -import { useWithSource } from '../../common/containers/source'; +import { useFetchIndex } from '../../common/containers/source'; import { EventsByDataset } from '../components/events_by_dataset'; import { EventCounts } from '../components/event_counts'; @@ -30,6 +30,9 @@ import { EndpointNotice } from '../components/endpoint_notice'; import { useMessagesStorage } from '../../common/containers/local_storage/use_messages_storage'; import { ENDPOINT_METADATA_INDEX } from '../../../common/constants'; import { useIngestEnabledCheck } from '../../common/hooks/endpoint/ingest_enabled'; +import { useSourcererScope } from '../../common/containers/sourcerer'; +import { Sourcerer } from '../../common/components/sourcerer'; +import { SourcererScopeName } from '../../common/store/sourcerer/model'; const DEFAULT_QUERY: Query = { query: '', language: 'kuery' }; const NO_FILTERS: Filter[] = []; @@ -43,17 +46,13 @@ const OverviewComponent: React.FC = ({ query = DEFAULT_QUERY, setAbsoluteRangeDatePicker, }) => { + const { from, deleteQuery, setQuery, to } = useGlobalTime(); + const { indicesExist, indexPattern, selectedPatterns } = useSourcererScope(); + const endpointMetadataIndex = useMemo(() => { return [ENDPOINT_METADATA_INDEX]; }, []); - - const { from, deleteQuery, setQuery, to } = useGlobalTime(); - const { indicesExist, indexPattern } = useWithSource(); - const { indicesExist: metadataIndexExists } = useWithSource( - 'default', - endpointMetadataIndex, - true - ); + const [, { indexExists: metadataIndexExists }] = useFetchIndex(endpointMetadataIndex, true); const { addMessage, hasMessage } = useMessagesStorage(); const hasDismissEndpointNoticeMessage: boolean = useMemo( () => hasMessage('management', 'dismissEndpointNotice'), @@ -81,6 +80,7 @@ const OverviewComponent: React.FC = ({ )} + @@ -107,6 +107,7 @@ const OverviewComponent: React.FC = ({ filters={filters} from={from} indexPattern={indexPattern} + indexNames={selectedPatterns} query={query} setQuery={setQuery} to={to} @@ -119,6 +120,7 @@ const OverviewComponent: React.FC = ({ filters={filters} from={from} indexPattern={indexPattern} + indexNames={selectedPatterns} query={query} setQuery={setQuery} to={to} @@ -129,6 +131,7 @@ const OverviewComponent: React.FC = ({ { - const docLinks = useKibana().services.docLinks; - + const { docLinks, http } = useKibana().services; + const basePath = http.basePath.get(); return ( @@ -39,7 +39,7 @@ export const Summary = React.memo(() => { ), data: ( - + { private kibanaVersion: string; @@ -385,15 +390,32 @@ export class Plugin implements IPlugin( + { indices: defaultIndicesName, onlyCheckIfIndicesExist: false }, + { + strategy: 'securitySolutionIndexFields', + } + ) + .toPromise(), + ]); - const { - detectionsSubPlugin, - hostsSubPlugin, - networkSubPlugin, - timelinesSubPlugin, - managementSubPlugin, - } = await this.downloadSubPlugins(); const { apolloClient } = composeLibs(coreStart); const appLibs: AppObservableLibs = { apolloClient, kibana: coreStart }; const libs$ = new BehaviorSubject(appLibs); @@ -417,12 +439,18 @@ export class Plugin implements IPlugin( ({ onOpen, show, dataProviders, timelineId }) => { const badgeCount = useMemo(() => getBadgeCount(dataProviders), [dataProviders]); - const { browserFields } = useWithSource(); + const { browserFields } = useSourcererScope(SourcererScopeName.timeline); if (!show) { return null; diff --git a/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.test.tsx index fe0f0c8f8b91f5..3814bc01bd9b1f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.test.tsx @@ -33,33 +33,33 @@ describe('useTimelineManager', () => { expect(isStringifiedComparisonEqual(uninitializedTimeline, timelineDefaults)).toBeTruthy(); }); }); - - it('getIndexToAddById', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useTimelineManager() - ); - await waitForNextUpdate(); - const data = result.current.getIndexToAddById(testId); - expect(data).toEqual(timelineDefaults.indexToAdd); - }); - }); - - it('setIndexToAdd', async () => { - await act(async () => { - const indexToAddArgs = { id: testId, indexToAdd: ['example'] }; - const { result, waitForNextUpdate } = renderHook(() => - useTimelineManager() - ); - await waitForNextUpdate(); - result.current.initializeTimeline({ - id: testId, - }); - result.current.setIndexToAdd(indexToAddArgs); - const data = result.current.getIndexToAddById(testId); - expect(data).toEqual(indexToAddArgs.indexToAdd); - }); - }); + // TO DO sourcerer + // it('getIndexToAddById', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook(() => + // useTimelineManager() + // ); + // await waitForNextUpdate(); + // const data = result.current.getIndexToAddById(testId); + // expect(data).toEqual(timelineDefaults.indexToAdd); + // }); + // }); + // + // it('setIndexToAdd', async () => { + // await act(async () => { + // const indexToAddArgs = { id: testId, indexToAdd: ['example'] }; + // const { result, waitForNextUpdate } = renderHook(() => + // useTimelineManager() + // ); + // await waitForNextUpdate(); + // result.current.initializeTimeline({ + // id: testId, + // }); + // result.current.setIndexToAdd(indexToAddArgs); + // const data = result.current.getIndexToAddById(testId); + // expect(data).toEqual(indexToAddArgs.indexToAdd); + // }); + // }); it('setIsTimelineLoading', async () => { await act(async () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx index f82158fe65c11b..4e1e877ec5b882 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/manage_timeline/index.tsx @@ -20,7 +20,6 @@ interface ManageTimelineInit { filterManager?: FilterManager; footerText?: string; id: string; - indexToAdd?: string[] | null; loadingText?: string; selectAll?: boolean; queryFields?: string[]; @@ -34,7 +33,6 @@ interface ManageTimeline { filterManager?: FilterManager; footerText: string; id: string; - indexToAdd: string[] | null; isLoading: boolean; loadingText: string; queryFields: string[]; @@ -58,11 +56,6 @@ type ActionManageTimeline = id: string; payload: boolean; } - | { - type: 'SET_INDEX_TO_ADD'; - id: string; - payload: string[]; - } | { type: 'SET_SELECT_ALL'; id: string; @@ -70,7 +63,6 @@ type ActionManageTimeline = }; export const getTimelineDefaults = (id: string) => ({ - indexToAdd: null, defaultModel: timelineDefaultModel, loadingText: i18n.LOADING_EVENTS, footerText: i18nF.TOTAL_COUNT_OF_EVENTS, @@ -96,14 +88,6 @@ const reducerManageTimeline = ( ...action.payload, }, } as ManageTimelineById; - case 'SET_INDEX_TO_ADD': - return { - ...state, - [action.id]: { - ...state[action.id], - indexToAdd: action.payload, - }, - } as ManageTimelineById; case 'SET_SELECT_ALL': return { ...state, @@ -127,12 +111,10 @@ const reducerManageTimeline = ( }; export interface UseTimelineManager { - getIndexToAddById: (id: string) => string[] | null; getManageTimelineById: (id: string) => ManageTimeline; getTimelineFilterManager: (id: string) => FilterManager | undefined; initializeTimeline: (newTimeline: ManageTimelineInit) => void; isManagedTimeline: (id: string) => boolean; - setIndexToAdd: (indexToAddArgs: { id: string; indexToAdd: string[] }) => void; setIsTimelineLoading: (isLoadingArgs: { id: string; isLoading: boolean }) => void; setSelectAll: (selectAllArgs: { id: string; selectAll: boolean }) => void; } @@ -163,14 +145,6 @@ export const useTimelineManager = ( [] ); - const setIndexToAdd = useCallback(({ id, indexToAdd }: { id: string; indexToAdd: string[] }) => { - dispatch({ - type: 'SET_INDEX_TO_ADD', - id, - payload: indexToAdd, - }); - }, []); - const setSelectAll = useCallback(({ id, selectAll }: { id: string; selectAll: boolean }) => { dispatch({ type: 'SET_SELECT_ALL', @@ -193,36 +167,23 @@ export const useTimelineManager = ( }, [initializeTimeline, state] ); - const getIndexToAddById = useCallback( - (id: string): string[] | null => { - if (state[id] != null) { - return state[id].indexToAdd; - } - return getTimelineDefaults(id).indexToAdd; - }, - [state] - ); const isManagedTimeline = useCallback((id: string): boolean => state[id] != null, [state]); return { - getIndexToAddById, getManageTimelineById, getTimelineFilterManager, initializeTimeline, isManagedTimeline, - setIndexToAdd, setIsTimelineLoading, setSelectAll, }; }; const init = { - getIndexToAddById: (id: string) => null, getManageTimelineById: (id: string) => getTimelineDefaults(id), getTimelineFilterManager: () => undefined, initializeTimeline: () => noop, isManagedTimeline: () => false, - setIndexToAdd: () => undefined, setIsTimelineLoading: () => noop, setSelectAll: () => noop, }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts index ed44fc14e3efae..c89114ec771387 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts @@ -273,6 +273,7 @@ describe('helpers', () => { highlightedDropAndProviderId: '', historyIds: [], id: 'savedObject-1', + indexNames: [], isFavorite: false, isLive: false, isSelectAllChecked: false, @@ -371,6 +372,7 @@ describe('helpers', () => { highlightedDropAndProviderId: '', historyIds: [], id: 'savedObject-1', + indexNames: [], isFavorite: false, isLive: false, isSelectAllChecked: false, @@ -469,6 +471,7 @@ describe('helpers', () => { highlightedDropAndProviderId: '', historyIds: [], id: 'savedObject-1', + indexNames: [], isFavorite: false, isLive: false, isSelectAllChecked: false, @@ -564,6 +567,7 @@ describe('helpers', () => { filters: [], highlightedDropAndProviderId: '', historyIds: [], + indexNames: [], id: 'savedObject-1', isFavorite: false, isLive: false, @@ -699,6 +703,7 @@ describe('helpers', () => { filters: [], highlightedDropAndProviderId: '', historyIds: [], + indexNames: [], isFavorite: false, isLive: false, isSelectAllChecked: false, @@ -865,6 +870,7 @@ describe('helpers', () => { ], highlightedDropAndProviderId: '', historyIds: [], + indexNames: [], isFavorite: false, isLive: false, isSelectAllChecked: false, diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts index b6b6148340a4a9..c89740f667b29b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts @@ -56,6 +56,8 @@ import { OpenTimelineResult, UpdateTimeline, DispatchUpdateTimeline } from './ty import { createNote } from '../notes/helpers'; import { IS_OPERATOR } from '../timeline/data_providers/data_provider'; import { normalizeTimeRange } from '../../../common/components/url_state/normalize_time_range'; +import { sourcererActions } from '../../../common/store/sourcerer'; +import { SourcererScopeName } from '../../../common/store/sourcerer/model'; export const OPEN_TIMELINE_CLASS_NAME = 'open-timeline'; @@ -375,6 +377,13 @@ export const dispatchUpdateTimeline = (dispatch: Dispatch): DispatchUpdateTimeli to, ruleNote, }: UpdateTimeline): (() => void) => () => { + dispatch( + sourcererActions.setSelectedIndexPatterns({ + id: SourcererScopeName.timeline, + selectedPatterns: timeline.indexNames, + eventType: timeline.eventType, + }) + ); dispatch(dispatchSetTimelineRangeDatePicker({ from, to })); dispatch(dispatchAddTimeline({ id, timeline, savedTimeline: duplicate })); if ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx index f681043a9047d9..dc824a8eb6272d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx @@ -5,12 +5,12 @@ */ import ApolloClient from 'apollo-client'; -import React, { useEffect, useState, useCallback } from 'react'; -import { connect, ConnectedProps } from 'react-redux'; +import React, { useEffect, useState, useCallback, useMemo } from 'react'; +import { connect, ConnectedProps, shallowEqual, useSelector } from 'react-redux'; import { Dispatch } from 'redux'; import { DeleteTimelineMutation, SortFieldTimeline, Direction } from '../../../graphql/types'; -import { State } from '../../../common/store'; +import { sourcererSelectors, State } from '../../../common/store'; import { TimelineId } from '../../../../common/types/timeline'; import { ColumnHeaderOptions, TimelineModel } from '../../../timelines/store/timeline/model'; import { timelineSelectors } from '../../../timelines/store/timeline'; @@ -110,6 +110,15 @@ export const StatefulOpenTimelineComponent = React.memo( /** The requested field to sort on */ const [sortField, setSortField] = useState(DEFAULT_SORT_FIELD); + const existingIndexNamesSelector = useMemo( + () => sourcererSelectors.getAllExistingIndexNamesSelector(), + [] + ); + const existingIndexNames = useSelector( + existingIndexNamesSelector, + shallowEqual + ); + const { customTemplateTimelineCount, defaultTimelineCount, @@ -193,7 +202,12 @@ export const StatefulOpenTimelineComponent = React.memo( const deleteTimelines: DeleteTimelines = useCallback( async (timelineIds: string[]) => { if (timelineIds.includes(timeline.savedObjectId || '')) { - createNewTimeline({ id: TimelineId.active, columns: defaultHeaders, show: false }); + createNewTimeline({ + id: TimelineId.active, + columns: defaultHeaders, + indexNames: existingIndexNames, + show: false, + }); } await apolloClient.mutate< @@ -206,7 +220,7 @@ export const StatefulOpenTimelineComponent = React.memo( }); refetch(); }, - [apolloClient, createNewTimeline, refetch, timeline] + [apolloClient, createNewTimeline, existingIndexNames, refetch, timeline] ); const onDeleteOneTimeline: OnDeleteOneTimeline = useCallback( @@ -382,12 +396,14 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ createNewTimeline: ({ id, columns, + indexNames, show, }: { id: string; columns: ColumnHeaderOptions[]; + indexNames: string[]; show?: boolean; - }) => dispatch(dispatchCreateNewTimeline({ id, columns, show })), + }) => dispatch(dispatchCreateNewTimeline({ id, columns, indexNames, show })), updateIsLoading: ({ id, isLoading }: { id: string; isLoading: boolean }) => dispatch(dispatchUpdateIsLoading({ id, isLoading })), updateTimeline: dispatchUpdateTimeline(dispatch), diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap index d76ddace40a5a4..18a648f2abfaab 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap @@ -806,9 +806,15 @@ In other use cases the message field can be used to concatenate different values } docValueFields={Array []} end="2018-03-24T03:33:52.253Z" - eventType="raw" filters={Array []} - id="foo" + id="test" + indexNames={ + Array [ + "filebeat-*", + "auditbeat-*", + "packetbeat-*", + ] + } indexPattern={ Object { "fields": Array [ @@ -900,9 +906,7 @@ In other use cases the message field can be used to concatenate different values "title": "filebeat-*,auditbeat-*,packetbeat-*", } } - indexToAdd={Array []} isLive={false} - isLoadingSource={false} isSaving={false} itemsPerPage={5} itemsPerPageOptions={ @@ -914,7 +918,7 @@ In other use cases the message field can be used to concatenate different values } kqlMode="search" kqlQueryExpression="" - loadingIndexName={false} + loadingSourcerer={false} onChangeItemsPerPage={[MockFunction]} onClose={[MockFunction]} onDataProviderEdited={[MockFunction]} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx index 824a37f72ba59c..cf9fbfaf19326d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx @@ -11,12 +11,15 @@ import { useDispatch } from 'react-redux'; import { Ecs } from '../../../../../common/ecs'; import { TimelineItem, TimelineNonEcsData } from '../../../../../common/search_strategy'; import { updateTimelineGraphEventId } from '../../../store/timeline/actions'; -import { EventType } from '../../../store/timeline/model'; +import { + TimelineEventsType, + TimelineTypeLiteral, + TimelineType, +} from '../../../../../common/types/timeline'; import { OnPinEvent, OnUnPinEvent } from '../events'; import { ActionIconItem } from './actions/action_icon_item'; import * as i18n from './translations'; -import { TimelineTypeLiteral, TimelineType } from '../../../../../common/types/timeline'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export const omitTypenameAndEmpty = (k: string, v: any): any | undefined => @@ -101,7 +104,7 @@ export const getEventIdToDataMapping = ( }, {}); /** Return eventType raw or signal */ -export const getEventType = (event: Ecs): Omit => { +export const getEventType = (event: Ecs): Omit => { if (!isEmpty(event.signal?.rule?.id)) { return 'signal'; } diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx index 58c87c086df6e2..fc0bcb134158cb 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx @@ -10,7 +10,7 @@ import { inputsModel } from '../../../../common/store'; import { BrowserFields, DocValueFields } from '../../../../common/containers/source'; import { TimelineItem, TimelineNonEcsData } from '../../../../../common/search_strategy'; import { Note } from '../../../../common/lib/note'; -import { ColumnHeaderOptions, EventType } from '../../../../timelines/store/timeline/model'; +import { ColumnHeaderOptions } from '../../../../timelines/store/timeline/model'; import { AddNoteToEvent, UpdateNote } from '../../notes/helpers'; import { OnColumnRemoved, @@ -31,7 +31,7 @@ import { RowRenderer } from './renderers/row_renderer'; import { Sort } from './sort'; import { GraphOverlay } from '../../graph_overlay'; import { DEFAULT_ICON_BUTTON_WIDTH } from '../helpers'; -import { TimelineId, TimelineType } from '../../../../../common/types/timeline'; +import { TimelineEventsType, TimelineId, TimelineType } from '../../../../../common/types/timeline'; export interface BodyProps { addNoteToEvent: AddNoteToEvent; @@ -45,7 +45,7 @@ export interface BodyProps { isEventViewer?: boolean; isSelectAllChecked: boolean; eventIdToNoteIds: Readonly>; - eventType?: EventType; + eventType?: TimelineEventsType; loadingEventIds: Readonly; onColumnRemoved: OnColumnRemoved; onColumnResized: OnColumnResized; @@ -68,7 +68,7 @@ export interface BodyProps { updateNote: UpdateNote; } -export const hasAdditionalActions = (id: string, eventType?: EventType): boolean => +export const hasAdditionalActions = (id: string, eventType?: TimelineEventsType): boolean => id === TimelineId.detectionsPage || id === TimelineId.detectionsRulesDetailsPage || ((id === TimelineId.active && eventType && ['all', 'signal', 'alert'].includes(eventType)) ?? diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/helpers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/helpers.test.tsx index 61b4c2b23c2673..d8a9f7528ddae8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/helpers.test.tsx @@ -112,7 +112,7 @@ describe('helpers', () => { }) ).toEqual({ updatedDestinationGroup: [sourceGroup[moveProviderFromSourceIndex]], - updatedSourceGroup: sourceGroup.filter((_, i) => i !== moveProviderFromSourceIndex), + updatedSourcererScope: sourceGroup.filter((_, i) => i !== moveProviderFromSourceIndex), }); }) ); @@ -138,7 +138,7 @@ describe('helpers', () => { : [...acc, sourceGroup[moveProviderFromSourceIndex], p], [] ), - updatedSourceGroup: sourceGroup.filter((_, i) => i !== moveProviderFromSourceIndex), + updatedSourcererScope: sourceGroup.filter((_, i) => i !== moveProviderFromSourceIndex), }); }) ) @@ -171,7 +171,7 @@ describe('helpers', () => { p.id !== sourceGroup[moveProviderFromSourceIndex].id || i === moveProviderToDestinationIndex ), - updatedSourceGroup: sourceGroup.filter((_, i) => i !== moveProviderFromSourceIndex), + updatedSourcererScope: sourceGroup.filter((_, i) => i !== moveProviderFromSourceIndex), }); }) ) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/helpers.tsx index 923ef86c0bbc0b..00c7f0705f3ce1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/helpers.tsx @@ -42,7 +42,7 @@ export const move = ({ sourceGroup: DataProvidersAnd[]; }): { updatedDestinationGroup: DataProvidersAnd[]; - updatedSourceGroup: DataProvidersAnd[]; + updatedSourcererScope: DataProvidersAnd[]; } => { const sourceClone = [...sourceGroup]; const destinationClone = [...destinationGroup]; @@ -56,7 +56,7 @@ export const move = ({ return { updatedDestinationGroup: deDuplicatedDestinationGroup, - updatedSourceGroup: sourceClone, + updatedSourcererScope: sourceClone, }; }; @@ -169,7 +169,7 @@ export const moveProvidersBetweenGroups = ({ const moveProviderFromSourceIndex = source.index; const moveProviderToDestinationIndex = destination.index; - const { updatedDestinationGroup, updatedSourceGroup } = move({ + const { updatedDestinationGroup, updatedSourcererScope } = move({ destinationGroup, moveProviderFromSourceIndex, moveProviderToDestinationIndex, @@ -180,7 +180,7 @@ export const moveProvidersBetweenGroups = ({ (acc, group, i) => [ ...acc, i === sourceGroupIndex - ? [...updatedSourceGroup] + ? [...updatedSourcererScope] : i === destinationGroupIndex ? [...updatedDestinationGroup] : [...group], diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx index f1bfdd5d33606d..d2737de7e75dcf 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx @@ -6,49 +6,32 @@ import { mount } from 'enzyme'; import React from 'react'; -import { MockedProvider } from 'react-apollo/test-utils'; -import { act } from 'react-dom/test-utils'; import useResizeObserver from 'use-resize-observer/polyfilled'; import '../../../common/mock/match_media'; +import { mockBrowserFields, mockDocValueFields } from '../../../common/containers/source/mock'; + import { - useSignalIndex, - ReturnSignalIndex, -} from '../../../detections/containers/detection_engine/alerts/use_signal_index'; -import { mocksSource } from '../../../common/containers/source/mock'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; -import { defaultHeaders, mockTimelineData, TestProviders } from '../../../common/mock'; -import { Direction } from '../../../graphql/types'; -import { timelineActions } from '../../store/timeline'; + mockIndexNames, + mockIndexPattern, + mockTimelineData, + TestProviders, +} from '../../../common/mock'; -import { Sort } from './body/sort'; -import { mockDataProviders } from './data_providers/mock/mock_data_providers'; -import { StatefulTimeline, Props as StatefulTimelineProps } from './index'; -import { Timeline } from './timeline'; -import { TimelineType, TimelineStatus } from '../../../../common/types/timeline'; +import { StatefulTimeline, OwnProps as StatefulTimelineOwnProps } from './index'; import { useTimelineEvents } from '../../containers/index'; jest.mock('../../containers/index', () => ({ useTimelineEvents: jest.fn(), })); -jest.mock('../../../common/lib/kibana', () => { - const originalModule = jest.requireActual('../../../common/lib/kibana'); - return { - ...originalModule, - useGetUserSavedObjectPermissions: jest.fn(), - }; -}); - +jest.mock('../../../common/lib/kibana'); jest.mock('../../../common/components/url_state/normalize_time_range.ts'); const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; jest.mock('use-resize-observer/polyfilled'); mockUseResizeObserver.mockImplementation(() => ({})); -const mockUseSignalIndex: jest.Mock = useSignalIndex as jest.Mock; -jest.mock('../../../detections/containers/detection_engine/alerts/use_signal_index'); jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); @@ -58,105 +41,37 @@ jest.mock('react-router-dom', () => { }; }); jest.mock('../flyout/header_with_close_button'); +jest.mock('../../../common/containers/sourcerer', () => { + const originalModule = jest.requireActual('../../../common/containers/sourcerer'); + + return { + ...originalModule, + useSourcererScope: jest.fn().mockReturnValue({ + browserFields: mockBrowserFields, + docValueFields: mockDocValueFields, + loading: false, + indexPattern: mockIndexPattern, + selectedPatterns: mockIndexNames, + }), + }; +}); describe('StatefulTimeline', () => { - let props = {} as StatefulTimelineProps; - const sort: Sort = { - columnId: '@timestamp', - sortDirection: Direction.desc, + const props: StatefulTimelineOwnProps = { + id: 'id', + onClose: jest.fn(), + usersViewing: [], }; - const startDate = '2018-03-23T18:49:23.132Z'; - const endDate = '2018-03-24T03:33:52.253Z'; - - const mocks = mocksSource; beforeEach(() => { (useTimelineEvents as jest.Mock).mockReturnValue([false, { events: mockTimelineData }]); - props = { - addProvider: timelineActions.addProvider, - columns: defaultHeaders, - createTimeline: timelineActions.createTimeline, - dataProviders: mockDataProviders, - eventType: 'raw', - end: endDate, - filters: [], - graphEventId: undefined, - id: 'foo', - isLive: false, - isSaving: false, - isTimelineExists: false, - itemsPerPage: 5, - itemsPerPageOptions: [5, 10, 20], - kqlMode: 'search', - kqlQueryExpression: '', - onClose: jest.fn(), - onDataProviderEdited: timelineActions.dataProviderEdited, - removeColumn: timelineActions.removeColumn, - removeProvider: timelineActions.removeProvider, - show: true, - showCallOutUnauthorizedMsg: false, - sort, - start: startDate, - status: TimelineStatus.active, - timelineType: TimelineType.default, - updateColumns: timelineActions.updateColumns, - updateDataProviderEnabled: timelineActions.updateDataProviderEnabled, - updateDataProviderExcluded: timelineActions.updateDataProviderExcluded, - updateDataProviderKqlQuery: timelineActions.updateDataProviderKqlQuery, - updateDataProviderType: timelineActions.updateDataProviderType, - updateHighlightedDropAndProviderId: timelineActions.updateHighlightedDropAndProviderId, - updateItemsPerPage: timelineActions.updateItemsPerPage, - updateItemsPerPageOptions: timelineActions.updateItemsPerPageOptions, - updateSort: timelineActions.updateSort, - upsertColumn: timelineActions.upsertColumn, - usersViewing: ['elastic'], - }; }); - describe('indexToAdd', () => { - test('Make sure that indexToAdd return an unknown index if signalIndex does not exist', async () => { - mockUseSignalIndex.mockImplementation(() => ({ - loading: false, - signalIndexExists: false, - signalIndexName: undefined, - })); - const wrapper = mount( - - - - - - ); - await act(async () => { - await waitFor(() => { - wrapper.update(); - const timeline = wrapper.find(Timeline); - expect(timeline.props().indexToAdd).toEqual([ - 'no-alert-index-049FC71A-4C2C-446F-9901-37XMC5024C51', - ]); - }); - }); - }); - - test('Make sure that indexToAdd return siem signal index if signalIndex exist', async () => { - mockUseSignalIndex.mockImplementation(() => ({ - loading: false, - signalIndexExists: true, - signalIndexName: 'mock-siem-signals-index', - })); - const wrapper = mount( - - - - - - ); - await act(async () => { - await waitFor(() => { - wrapper.update(); - const timeline = wrapper.find(Timeline); - expect(timeline.props().indexToAdd).toEqual(['mock-siem-signals-index']); - }); - }); - }); + test('renders ', () => { + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="timeline"]')).toBeTruthy(); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx index c170c93ee60832..ccdb0793bc03f0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx @@ -5,13 +5,10 @@ */ import { isEmpty } from 'lodash/fp'; -import React, { useEffect, useCallback, useMemo } from 'react'; +import React, { useEffect, useCallback } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import deepEqual from 'fast-deep-equal'; -import { NO_ALERT_INDEX } from '../../../../common/constants'; -import { useWithSource } from '../../../common/containers/source'; -import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index'; import { inputsModel, inputsSelectors, State } from '../../../common/store'; import { timelineActions, timelineSelectors } from '../../store/timeline'; import { ColumnHeaderOptions, TimelineModel } from '../../../timelines/store/timeline/model'; @@ -26,6 +23,8 @@ import { OnToggleDataProviderType, } from './events'; import { Timeline } from './timeline'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; +import { SourcererScopeName } from '../../../common/store/sourcerer/model'; export interface OwnProps { id: string; @@ -40,7 +39,6 @@ const StatefulTimelineComponent = React.memo( columns, createTimeline, dataProviders, - eventType, end, filters, graphEventId, @@ -69,19 +67,13 @@ const StatefulTimelineComponent = React.memo( upsertColumn, usersViewing, }) => { - const { loading, signalIndexExists, signalIndexName } = useSignalIndex(); - - const indexToAdd = useMemo(() => { - if ( - eventType && - signalIndexExists && - signalIndexName != null && - ['signal', 'alert', 'all'].includes(eventType) - ) { - return [signalIndexName]; - } - return [NO_ALERT_INDEX]; // Following index does not exist so we won't show any events; - }, [eventType, signalIndexExists, signalIndexName]); + const { + browserFields, + docValueFields, + loading, + indexPattern, + selectedPatterns, + } = useSourcererScope(SourcererScopeName.timeline); const onDataProviderRemoved: OnDataProviderRemoved = useCallback( (providerId: string, andProviderId?: string) => @@ -160,22 +152,16 @@ const StatefulTimelineComponent = React.memo( }); } }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [columns, id] + [columns, id, removeColumn, upsertColumn] ); useEffect(() => { if (createTimeline != null && !isTimelineExists) { - createTimeline({ id, columns: defaultHeaders, show: false }); + createTimeline({ id, columns: defaultHeaders, indexNames: selectedPatterns, show: false }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const { docValueFields, indexPattern, browserFields, loading: isLoadingSource } = useWithSource( - 'default', - indexToAdd - ); - return ( ( dataProviders={dataProviders!} docValueFields={docValueFields} end={end} - eventType={eventType} filters={filters} graphEventId={graphEventId} id={id} indexPattern={indexPattern} - indexToAdd={indexToAdd} + indexNames={selectedPatterns} isLive={isLive} - isLoadingSource={isLoadingSource} isSaving={isSaving} itemsPerPage={itemsPerPage!} itemsPerPageOptions={itemsPerPageOptions!} kqlMode={kqlMode} kqlQueryExpression={kqlQueryExpression} - loadingIndexName={loading} + loadingSourcerer={loading} onChangeItemsPerPage={onChangeItemsPerPage} onClose={onClose} onDataProviderEdited={onDataProviderEditedLocal} @@ -215,10 +199,8 @@ const StatefulTimelineComponent = React.memo( /> ); }, - // eslint-disable-next-line complexity (prevProps, nextProps) => { return ( - prevProps.eventType === nextProps.eventType && prevProps.end === nextProps.end && prevProps.graphEventId === nextProps.graphEventId && prevProps.id === nextProps.id && diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.test.tsx index 5b3bc72fc37ca3..7da3cf940da50f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.test.tsx @@ -93,15 +93,18 @@ describe('useCreateTimelineButton', () => { wrapper.find('[data-test-subj="timeline-new"]').first().simulate('click'); expect(mockDispatch.mock.calls[0][0].type).toEqual( - 'x-pack/security_solution/local/timeline/CREATE_TIMELINE' + 'x-pack/security_solution/local/sourcerer/SET_SELECTED_INDEX_PATTERNS' ); expect(mockDispatch.mock.calls[1][0].type).toEqual( - 'x-pack/security_solution/local/inputs/ADD_GLOBAL_LINK_TO' + 'x-pack/security_solution/local/timeline/CREATE_TIMELINE' ); expect(mockDispatch.mock.calls[2][0].type).toEqual( - 'x-pack/security_solution/local/inputs/ADD_TIMELINE_LINK_TO' + 'x-pack/security_solution/local/inputs/ADD_GLOBAL_LINK_TO' ); expect(mockDispatch.mock.calls[3][0].type).toEqual( + 'x-pack/security_solution/local/inputs/ADD_TIMELINE_LINK_TO' + ); + expect(mockDispatch.mock.calls[4][0].type).toEqual( 'x-pack/security_solution/local/inputs/SET_RELATIVE_RANGE_DATE_PICKER' ); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx index 97f3b1df011ffd..3919ee21b2a907 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx @@ -3,8 +3,8 @@ * 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, { useCallback } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import React, { useCallback, useMemo } from 'react'; +import { shallowEqual, useDispatch, useSelector } from 'react-redux'; import { EuiButton, EuiButtonEmpty } from '@elastic/eui'; import { defaultHeaders } from '../body/column_headers/default_headers'; import { timelineActions } from '../../../store/timeline'; @@ -15,6 +15,9 @@ import { TimelineTypeLiteral, } from '../../../../../common/types/timeline'; import { inputsActions, inputsSelectors } from '../../../../common/store/inputs'; +import { sourcererActions, sourcererSelectors } from '../../../../common/store/sourcerer'; +import { State } from '../../../../common/store'; +import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; export const useCreateTimelineButton = ({ timelineId, @@ -26,6 +29,11 @@ export const useCreateTimelineButton = ({ closeGearMenu?: () => void; }) => { const dispatch = useDispatch(); + const existingIndexNamesSelector = useMemo( + () => sourcererSelectors.getAllExistingIndexNamesSelector(), + [] + ); + const existingIndexNames = useSelector(existingIndexNamesSelector, shallowEqual); const { timelineFullScreen, setTimelineFullScreen } = useFullScreen(); const globalTimeRange = useSelector(inputsSelectors.globalTimeRangeSelector); const createTimeline = useCallback( @@ -33,12 +41,19 @@ export const useCreateTimelineButton = ({ if (id === TimelineId.active && timelineFullScreen) { setTimelineFullScreen(false); } + dispatch( + sourcererActions.setSelectedIndexPatterns({ + id: SourcererScopeName.timeline, + selectedPatterns: existingIndexNames, + }) + ); dispatch( timelineActions.createTimeline({ id, columns: defaultHeaders, show, timelineType, + indexNames: existingIndexNames, }) ); dispatch(inputsActions.addGlobalLinkTo({ linkToId: 'timeline' })); @@ -59,7 +74,14 @@ export const useCreateTimelineButton = ({ ); } }, - [dispatch, globalTimeRange, setTimelineFullScreen, timelineFullScreen, timelineType] + [ + existingIndexNames, + dispatch, + globalTimeRange, + setTimelineFullScreen, + timelineFullScreen, + timelineType, + ] ); const handleButtonClick = useCallback(() => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx index 7ee7e12c0ef628..166705128ce022 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx @@ -24,11 +24,14 @@ import { inputsModel, inputsSelectors, } from '../../../../common/store'; +import { TimelineEventsType } from '../../../../../common/types/timeline'; import { timelineActions, timelineSelectors } from '../../../store/timeline'; -import { KqlMode, TimelineModel, EventType } from '../../../../timelines/store/timeline/model'; +import { KqlMode, TimelineModel } from '../../../../timelines/store/timeline/model'; import { timelineDefaults } from '../../../../timelines/store/timeline/defaults'; import { dispatchUpdateReduxTime } from '../../../../common/components/super_date_picker'; import { SearchOrFilter } from './search_or_filter'; +import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; +import { sourcererActions } from '../../../../common/store/sourcerer'; interface OwnProps { browserFields: BrowserFields; @@ -62,7 +65,7 @@ const StatefulSearchOrFilterComponent = React.memo( timelineId, to, toStr, - updateEventType, + updateEventTypeAndIndexesName, updateKqlMode, updateReduxTime, }) => { @@ -111,13 +114,14 @@ const StatefulSearchOrFilterComponent = React.memo( [timelineId, setSavedQueryId] ); - const handleUpdateEventType = useCallback( - (newEventType: EventType) => - updateEventType({ + const handleUpdateEventTypeAndIndexesName = useCallback( + (newEventType: TimelineEventsType, indexNames: string[]) => + updateEventTypeAndIndexesName({ id: timelineId, eventType: newEventType, + indexNames, }), - [timelineId, updateEventType] + [timelineId, updateEventTypeAndIndexesName] ); return ( @@ -143,7 +147,7 @@ const StatefulSearchOrFilterComponent = React.memo( timelineId={timelineId} to={to} toStr={toStr} - updateEventType={handleUpdateEventType} + updateEventTypeAndIndexesName={handleUpdateEventTypeAndIndexesName} updateKqlMode={updateKqlMode!} updateReduxTime={updateReduxTime} /> @@ -211,8 +215,24 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ filterQuery, }) ), - updateEventType: ({ id, eventType }: { id: string; eventType: EventType }) => - dispatch(timelineActions.updateEventType({ id, eventType })), + updateEventTypeAndIndexesName: ({ + id, + eventType, + indexNames, + }: { + id: string; + eventType: TimelineEventsType; + indexNames: string[]; + }) => { + dispatch(timelineActions.updateEventType({ id, eventType })); + dispatch(timelineActions.updateIndexNames({ id, indexNames })); + dispatch( + sourcererActions.setSelectedIndexPatterns({ + id: SourcererScopeName.timeline, + selectedPatterns: indexNames, + }) + ); + }, updateKqlMode: ({ id, kqlMode }: { id: string; kqlMode: KqlMode }) => dispatch(timelineActions.updateKqlMode({ id, kqlMode })), setKqlFilterQueryDraft: ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.tsx index fc2bd1c21abdc7..16200f4e5ef9a7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.tsx @@ -4,17 +4,47 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiHealth, EuiSuperSelect } from '@elastic/eui'; -import React, { memo } from 'react'; +import { + EuiAccordion, + EuiButton, + EuiButtonEmpty, + EuiRadioGroup, + EuiComboBox, + EuiComboBoxOptionOption, + EuiHealth, + EuiIcon, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiPopoverTitle, + EuiSpacer, + EuiText, + EuiToolTip, +} from '@elastic/eui'; +import deepEqual from 'fast-deep-equal'; +import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; +import { useSelector } from 'react-redux'; import styled from 'styled-components'; -import { EventType } from '../../../../timelines/store/timeline/model'; +import { State } from '../../../../common/store'; +import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; +import { TimelineEventsType } from '../../../../../common/types/timeline'; +import { getSourcererScopeSelector, SourcererScopeSelector } from './selectors'; import * as i18n from './translations'; -interface EventTypeOptionItem { - value: EventType; - inputDisplay: React.ReactElement; -} +const PopoverContent = styled.div` + width: 600px; +`; + +const ResetButton = styled(EuiButtonEmpty)` + width: fit-content; +`; + +const MyEuiButton = styled(EuiButton)` + .euiHealth { + vertical-align: middle; + } +`; const AllEuiHealth = styled(EuiHealth)` margin-left: -2px; @@ -36,6 +66,18 @@ const WarningEuiHealth = styled(EuiHealth)` } `; +const AdvancedSettings = styled(EuiText)` + color: ${({ theme }) => theme.eui.euiColorPrimary}; +`; + +const ConfigHelper = styled(EuiText)` + margin-left: 4px; +`; + +const Filter = styled(EuiRadioGroup)` + margin-left: 4px; +`; + const PickEventContainer = styled.div` .euiSuperSelect { width: 170px; @@ -46,43 +88,309 @@ const PickEventContainer = styled.div` } `; -export const eventTypeOptions: EventTypeOptionItem[] = [ +const getEventTypeOptions = (isCustomDisabled: boolean = true) => [ { - value: 'all', - inputDisplay: ( + id: 'all', + label: ( {i18n.ALL_EVENT} ), }, { - value: 'raw', - inputDisplay: {i18n.RAW_EVENT}, + id: 'raw', + label: {i18n.RAW_EVENT}, }, { - value: 'alert', - inputDisplay: {i18n.DETECTION_ALERTS_EVENT}, + id: 'alert', + label: {i18n.DETECTION_ALERTS_EVENT}, + }, + { + id: 'custom', + label: <>{i18n.CUSTOM_INDEX_PATTERNS}, + disabled: isCustomDisabled, }, ]; interface PickEventTypeProps { - eventType: EventType; - onChangeEventType: (value: EventType) => void; + eventType: TimelineEventsType; + onChangeEventTypeAndIndexesName: (value: TimelineEventsType, indexNames: string[]) => void; } const PickEventTypeComponents: React.FC = ({ - eventType, - onChangeEventType, + eventType = 'all', + onChangeEventTypeAndIndexesName, }) => { + const [isPopoverOpen, setPopover] = useState(false); + const [showAdvanceSettings, setAdvanceSettings] = useState(eventType === 'custom'); + const [filterEventType, setFilterEventType] = useState(eventType); + const sourcererScopeSelector = useMemo(getSourcererScopeSelector, []); + const { configIndexPatterns, kibanaIndexPatterns, signalIndexName, sourcererScope } = useSelector< + State, + SourcererScopeSelector + >((state) => sourcererScopeSelector(state, SourcererScopeName.timeline), deepEqual); + const [selectedOptions, setSelectedOptions] = useState>>( + sourcererScope.selectedPatterns.map((indexSelected) => ({ + label: indexSelected, + value: indexSelected, + })) + ); + + const indexesPatternOptions = useMemo( + () => + [ + ...configIndexPatterns, + ...kibanaIndexPatterns.map((kip) => kip.title), + signalIndexName, + ].reduce>>((acc, index) => { + if (index != null && !acc.some((o) => o.label.includes(index))) { + return [...acc, { label: index, value: index }]; + } + return acc; + }, []), + [configIndexPatterns, kibanaIndexPatterns, signalIndexName] + ); + + const renderOption = useCallback( + (option) => { + const { value } = option; + if (kibanaIndexPatterns.some((kip) => kip.title === value)) { + return ( + <> + {value} + + ); + } + return <>{value}; + }, + [kibanaIndexPatterns] + ); + + const onChangeCombo = useCallback( + (newSelectedOptions: Array>) => { + const localSelectedPatterns = newSelectedOptions.map((nso) => nso.label); + if ( + localSelectedPatterns.sort().join() === + [...configIndexPatterns, signalIndexName].sort().join() + ) { + setFilterEventType('all'); + } else if (localSelectedPatterns.sort().join() === configIndexPatterns.sort().join()) { + setFilterEventType('raw'); + } else if (localSelectedPatterns.sort().join() === signalIndexName) { + setFilterEventType('alert'); + } else { + setFilterEventType('custom'); + } + + setSelectedOptions(newSelectedOptions); + }, + [configIndexPatterns, signalIndexName] + ); + + const onChangeFilter = useCallback( + (filter) => { + setFilterEventType(filter); + if (filter === 'all') { + setSelectedOptions( + [...configIndexPatterns, signalIndexName ?? ''].map((indexSelected) => ({ + label: indexSelected, + value: indexSelected, + })) + ); + } else if (filter === 'raw') { + setSelectedOptions( + configIndexPatterns.map((indexSelected) => ({ + label: indexSelected, + value: indexSelected, + })) + ); + } else if (filter === 'alert') { + setSelectedOptions([ + { + label: signalIndexName ?? '', + value: signalIndexName ?? '', + }, + ]); + } else if (filter === 'kibana') { + setSelectedOptions( + kibanaIndexPatterns.map((kip) => ({ + label: kip.title, + value: kip.title, + })) + ); + } + }, + [configIndexPatterns, kibanaIndexPatterns, signalIndexName] + ); + + const togglePopover = useCallback( + () => setPopover((prevIsPopoverOpen) => !prevIsPopoverOpen), + [] + ); + + const closePopover = useCallback(() => setPopover(false), []); + + const handleSaveIndices = useCallback(() => { + onChangeEventTypeAndIndexesName( + filterEventType, + selectedOptions.map((so) => so.label) + ); + setPopover(false); + }, [filterEventType, onChangeEventTypeAndIndexesName, selectedOptions]); + + const resetDataSources = useCallback(() => { + setSelectedOptions( + sourcererScope.selectedPatterns.map((indexSelected) => ({ + label: indexSelected, + value: indexSelected, + })) + ); + setFilterEventType(eventType); + }, [eventType, sourcererScope.selectedPatterns]); + + const comboBox = useMemo( + () => ( + + ), + [onChangeCombo, indexesPatternOptions, renderOption, selectedOptions] + ); + + const filterOptions = useMemo(() => getEventTypeOptions(filterEventType !== 'custom'), [ + filterEventType, + ]); + + const filter = useMemo( + () => ( + + ), + [filterEventType, filterOptions, onChangeFilter] + ); + + const button = useMemo(() => { + const options = getEventTypeOptions(); + return ( + + {options.find((opt) => opt.id === eventType)?.label} + + ); + }, [eventType, sourcererScope.loading, togglePopover]); + + const tooltipContent = useMemo( + () => (isPopoverOpen ? null : sourcererScope.selectedPatterns.sort().join(', ')), + [isPopoverOpen, sourcererScope.selectedPatterns] + ); + + const ButtonContent = useMemo( + () => ( + + {showAdvanceSettings + ? i18n.HIDE_INDEX_PATTERNS_ADVANCED_SETTINGS + : i18n.SHOW_INDEX_PATTERNS_ADVANCED_SETTINGS} + + ), + [showAdvanceSettings] + ); + + useEffect(() => { + const newSelectedOptions = sourcererScope.selectedPatterns.map((indexSelected) => ({ + label: indexSelected, + value: indexSelected, + })); + setSelectedOptions((prevSelectedOptions) => { + if (!deepEqual(newSelectedOptions, prevSelectedOptions)) { + return newSelectedOptions; + } + return prevSelectedOptions; + }); + }, [sourcererScope.selectedPatterns]); + + useEffect(() => { + setFilterEventType((prevFilter) => (prevFilter !== eventType ? eventType : prevFilter)); + setAdvanceSettings(eventType === 'custom'); + }, [eventType]); + return ( - + + + + + <>{i18n.SELECT_INDEX_PATTERNS} + + + {filter} + + + <> + + {comboBox} + + + {!showAdvanceSettings && ( + <> + + + {i18n.CONFIGURE_INDEX_PATTERNS} + + + )} + + + + + {i18n.DATA_SOURCES_RESET} + + + + + {i18n.SAVE_INDEX_PATTERNS} + + + + + + ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx index e04cef4ad8d934..32a516497f6070 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx @@ -15,7 +15,8 @@ import { } from '../../../../../../../../src/plugins/data/public'; import { BrowserFields } from '../../../../common/containers/source'; import { KueryFilterQuery, KueryFilterQueryKind } from '../../../../common/store'; -import { KqlMode, EventType } from '../../../../timelines/store/timeline/model'; +import { TimelineEventsType } from '../../../../../common/types/timeline'; +import { KqlMode } from '../../../../timelines/store/timeline/model'; import { DispatchUpdateReduxTime } from '../../../../common/components/super_date_picker'; import { DataProvider } from '../data_providers/data_provider'; import { QueryBarTimeline } from '../query_bar'; @@ -47,7 +48,7 @@ interface Props { applyKqlFilterQuery: (expression: string, kind: KueryFilterQueryKind) => void; browserFields: BrowserFields; dataProviders: DataProvider[]; - eventType: EventType; + eventType: TimelineEventsType; filterManager: FilterManager; filterQuery: KueryFilterQuery; filterQueryDraft: KueryFilterQuery; @@ -66,7 +67,7 @@ interface Props { savedQueryId: string | null; to: string; toStr: string; - updateEventType: (eventType: EventType) => void; + updateEventTypeAndIndexesName: (eventType: TimelineEventsType, indexNames: string[]) => void; updateReduxTime: DispatchUpdateReduxTime; } @@ -114,7 +115,7 @@ export const SearchOrFilter = React.memo( setSavedQueryId, to, toStr, - updateEventType, + updateEventTypeAndIndexesName, updateKqlMode, updateReduxTime, }) => { @@ -167,7 +168,10 @@ export const SearchOrFilter = React.memo( /> - + diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/selectors.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/selectors.tsx new file mode 100644 index 00000000000000..2fdcf7a0eb0c12 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/selectors.tsx @@ -0,0 +1,43 @@ +/* + * 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 { State } from '../../../../common/store'; +import { sourcererSelectors } from '../../../../common/store/selectors'; +import { + KibanaIndexPatterns, + ManageScope, + SourcererScopeName, +} from '../../../../common/store/sourcerer/model'; + +export interface SourcererScopeSelector { + configIndexPatterns: string[]; + kibanaIndexPatterns: KibanaIndexPatterns; + signalIndexName: string | null; + sourcererScope: ManageScope; +} + +export const getSourcererScopeSelector = () => { + const getkibanaIndexPatternsSelector = sourcererSelectors.kibanaIndexPatternsSelector(); + const getScopesSelector = sourcererSelectors.scopesSelector(); + const getConfigIndexPatternsSelector = sourcererSelectors.configIndexPatternsSelector(); + const getSignalIndexNameSelector = sourcererSelectors.signalIndexNameSelector(); + + const mapStateToProps = (state: State, scopeId: SourcererScopeName): SourcererScopeSelector => { + const kibanaIndexPatterns = getkibanaIndexPatternsSelector(state); + const scope = getScopesSelector(state)[scopeId]; + const configIndexPatterns = getConfigIndexPatternsSelector(state); + const signalIndexName = getSignalIndexNameSelector(state); + + return { + kibanaIndexPatterns, + configIndexPatterns, + signalIndexName, + sourcererScope: scope, + }; + }; + + return mapStateToProps; +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/translations.ts index b5c78c458697c9..f595881a57d03a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/translations.ts @@ -73,14 +73,14 @@ export const FILTER_OR_SEARCH_WITH_KQL = i18n.translate( export const ALL_EVENT = i18n.translate( 'xpack.securitySolution.timeline.searchOrFilter.eventTypeAllEvent', { - defaultMessage: 'All', + defaultMessage: 'All data sources', } ); export const RAW_EVENT = i18n.translate( 'xpack.securitySolution.timeline.searchOrFilter.eventTypeRawEvent', { - defaultMessage: 'Raw events', + defaultMessage: 'Events', } ); @@ -90,3 +90,59 @@ export const DETECTION_ALERTS_EVENT = i18n.translate( defaultMessage: 'Detection Alerts', } ); + +export const CUSTOM_INDEX_PATTERNS = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.customeIndexNames', + { + defaultMessage: 'Custom', + } +); + +export const SELECT_INDEX_PATTERNS = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.indexPatterns.help', + { + defaultMessage: 'Data sources selection', + } +); + +export const CONFIGURE_INDEX_PATTERNS = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.indexPatterns.configure', + { + defaultMessage: 'View data sources associated with each of the above selections', + } +); + +export const SAVE_INDEX_PATTERNS = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.indexPatterns.save', + { + defaultMessage: 'Save', + } +); + +export const SHOW_INDEX_PATTERNS_ADVANCED_SETTINGS = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.indexPatterns.showAdvancedSettings', + { + defaultMessage: 'Show Advanced', + } +); + +export const HIDE_INDEX_PATTERNS_ADVANCED_SETTINGS = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.indexPatterns.hideAdvancedSettings', + { + defaultMessage: 'Hide Advanced', + } +); + +export const DATA_SOURCES_RESET = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.indexPatterns.resetSettings', + { + defaultMessage: 'Reset', + } +); + +export const PICK_INDEX_PATTERNS = i18n.translate( + 'xpack.securitySolution.timeline.searchOrFilter.indexPatterns.pickIndexPatternsCombo', + { + defaultMessage: 'Pick index patterns', + } +); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx index 234814a68877d5..e898779eacce96 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx @@ -8,7 +8,7 @@ import { EuiLoadingSpinner } from '@elastic/eui'; import { rgba } from 'polished'; import styled, { createGlobalStyle } from 'styled-components'; -import { EventType } from '../../../timelines/store/timeline/model'; +import { TimelineEventsType } from '../../../../common/types/timeline'; import { IS_TIMELINE_FIELD_DRAGGING_CLASS_NAME } from '../../../common/components/drag_and_drop/helpers'; /** @@ -173,7 +173,7 @@ export const EventsTbody = styled.div.attrs(({ className = '' }) => ({ export const EventsTrGroup = styled.div.attrs(({ className = '' }) => ({ className: `siemEventsTable__trGroup ${className}`, -}))<{ className?: string; eventType: Omit; showLeftBorder: boolean }>` +}))<{ className?: string; eventType: Omit; showLeftBorder: boolean }>` border-bottom: ${({ theme }) => theme.eui.euiBorderWidthThin} solid ${({ theme }) => theme.eui.euiColorLightShade}; ${({ theme, eventType, showLeftBorder }) => diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx index 887a6a546d2b77..bde1e7bf5829ad 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx @@ -10,7 +10,12 @@ import useResizeObserver from 'use-resize-observer/polyfilled'; import { mockBrowserFields } from '../../../common/containers/source/mock'; import { Direction } from '../../../graphql/types'; -import { defaultHeaders, mockTimelineData, mockIndexPattern } from '../../../common/mock'; +import { + defaultHeaders, + mockTimelineData, + mockIndexPattern, + mockIndexNames, +} from '../../../common/mock'; import '../../../common/mock/match_media'; import { TestProviders } from '../../../common/mock/test_providers'; @@ -23,7 +28,7 @@ import { TimelineComponent, Props as TimelineComponentProps } from './timeline'; import { Sort } from './body/sort'; import { mockDataProviders } from './data_providers/mock/mock_data_providers'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; -import { TimelineStatus, TimelineType } from '../../../../common/types/timeline'; +import { TimelineId, TimelineStatus, TimelineType } from '../../../../common/types/timeline'; import { useTimelineEvents } from '../../containers/index'; import { useTimelineEventsDetails } from '../../containers/details/index'; @@ -94,22 +99,20 @@ describe('Timeline', () => { props = { browserFields: mockBrowserFields, columns: defaultHeaders, - id: 'foo', dataProviders: mockDataProviders, docValueFields: [], end: endDate, - eventType: 'raw' as TimelineComponentProps['eventType'], filters: [], + id: TimelineId.test, + indexNames: mockIndexNames, indexPattern, - indexToAdd: [], isLive: false, - isLoadingSource: false, isSaving: false, itemsPerPage: 5, itemsPerPageOptions: [5, 10, 20], kqlMode: 'search' as TimelineComponentProps['kqlMode'], kqlQueryExpression: '', - loadingIndexName: false, + loadingSourcerer: false, onChangeItemsPerPage: jest.fn(), onClose: jest.fn(), onDataProviderEdited: jest.fn(), @@ -119,12 +122,12 @@ describe('Timeline', () => { onToggleDataProviderType: jest.fn(), show: true, showCallOutUnauthorizedMsg: false, - start: startDate, sort, + start: startDate, status: TimelineStatus.active, + timelineType: TimelineType.default, toggleColumn: jest.fn(), usersViewing: ['elastic'], - timelineType: TimelineType.default, }; }); @@ -174,7 +177,7 @@ describe('Timeline', () => { test('it does NOT render the timeline table when the source is loading', () => { const wrapper = mount( - + ); @@ -211,16 +214,6 @@ describe('Timeline', () => { expect(wrapper.find('[data-test-subj="table-pagination"]').exists()).toEqual(false); }); - test('it defaults to showing `All`', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find('[data-test-subj="pick-event-type"] button').text()).toEqual('All'); - }); - it('it shows the timeline footer', () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx index 434c7075a94709..d1a25e6f3e1a48 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx @@ -14,7 +14,7 @@ import { BrowserFields, DocValueFields } from '../../../common/containers/source import { Direction } from '../../../../common/search_strategy'; import { useTimelineEvents } from '../../containers/index'; import { useKibana } from '../../../common/lib/kibana'; -import { ColumnHeaderOptions, KqlMode, EventType } from '../../../timelines/store/timeline/model'; +import { ColumnHeaderOptions, KqlMode } from '../../../timelines/store/timeline/model'; import { defaultHeaders } from './body/column_headers/default_headers'; import { Sort } from './body/sort'; import { StatefulBody } from './body/stateful_body'; @@ -99,20 +99,18 @@ export interface Props { dataProviders: DataProvider[]; docValueFields: DocValueFields[]; end: string; - eventType?: EventType; filters: Filter[]; graphEventId?: string; id: string; + indexNames: string[]; indexPattern: IIndexPattern; - indexToAdd: string[]; isLive: boolean; - isLoadingSource: boolean; isSaving: boolean; itemsPerPage: number; itemsPerPageOptions: number[]; kqlMode: KqlMode; kqlQueryExpression: string; - loadingIndexName: boolean; + loadingSourcerer: boolean; onChangeItemsPerPage: OnChangeItemsPerPage; onClose: () => void; onDataProviderEdited: OnDataProviderEdited; @@ -122,12 +120,12 @@ export interface Props { onToggleDataProviderType: OnToggleDataProviderType; show: boolean; showCallOutUnauthorizedMsg: boolean; - start: string; sort: Sort; + start: string; status: TimelineStatusLiteral; + timelineType: TimelineType; toggleColumn: (column: ColumnHeaderOptions) => void; usersViewing: string[]; - timelineType: TimelineType; } /** The parent Timeline component */ @@ -137,20 +135,18 @@ export const TimelineComponent: React.FC = ({ dataProviders, docValueFields, end, - eventType, filters, graphEventId, id, indexPattern, - indexToAdd, + indexNames, isLive, - isLoadingSource, + loadingSourcerer, isSaving, itemsPerPage, itemsPerPageOptions, kqlMode, kqlQueryExpression, - loadingIndexName, onChangeItemsPerPage, onClose, onDataProviderEdited, @@ -204,11 +200,11 @@ export const TimelineComponent: React.FC = ({ const canQueryTimeline = useMemo( () => combinedQueries != null && - isLoadingSource != null && - !isLoadingSource && + loadingSourcerer != null && + !loadingSourcerer && !isEmpty(start) && !isEmpty(end), - [isLoadingSource, combinedQueries, start, end] + [loadingSourcerer, combinedQueries, start, end] ); const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; const timelineQueryFields = useMemo(() => { @@ -223,16 +219,13 @@ export const TimelineComponent: React.FC = ({ [sort.columnId, sort.sortDirection] ); const [isQueryLoading, setIsQueryLoading] = useState(false); - const { initializeTimeline, setIndexToAdd, setIsTimelineLoading } = useManageTimeline(); - + const { initializeTimeline, setIsTimelineLoading } = useManageTimeline(); useEffect(() => { initializeTimeline({ filterManager, id, - indexToAdd, }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [initializeTimeline, filterManager, id]); const [ loading, @@ -240,24 +233,19 @@ export const TimelineComponent: React.FC = ({ ] = useTimelineEvents({ docValueFields, endDate: end, - eventType, id, - indexToAdd, + indexNames, fields: timelineQueryFields, limit: itemsPerPage, filterQuery: combinedQueries?.filterQuery ?? '', startDate: start, - skip: canQueryTimeline, + skip: !canQueryTimeline, sort: timelineQuerySortField, }); useEffect(() => { - setIsTimelineLoading({ id, isLoading: isQueryLoading || loadingIndexName }); - }, [loadingIndexName, id, isQueryLoading, setIsTimelineLoading]); - - useEffect(() => { - setIndexToAdd({ id, indexToAdd }); - }, [id, indexToAdd, setIndexToAdd]); + setIsTimelineLoading({ id, isLoading: isQueryLoading || loadingSourcerer }); + }, [loadingSourcerer, id, isQueryLoading, setIsTimelineLoading]); useEffect(() => { setIsQueryLoading(loading); @@ -329,7 +317,7 @@ export const TimelineComponent: React.FC = ({ height={footerHeight} id={id} isLive={isLive} - isLoading={loading || loadingIndexName} + isLoading={loading || loadingSourcerer} itemsCount={events.length} itemsPerPage={itemsPerPage} itemsPerPageOptions={itemsPerPageOptions} diff --git a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx index 23c05805a5aa4f..cd72ffb8ac8030 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx @@ -9,7 +9,6 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; import { inputsModel } from '../../../common/store'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { useKibana } from '../../../common/lib/kibana'; import { DocValueFields, @@ -36,10 +35,9 @@ export const useTimelineEventsDetails = ({ eventId, skip, }: UseTimelineEventsDetailsProps): [boolean, EventsArgs['detailsData']] => { - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [ timelineDetailsRequest, @@ -102,7 +100,6 @@ export const useTimelineEventsDetails = ({ setTimelineDetailsRequest((prevRequest) => { const myRequest = { ...(prevRequest ?? {}), - defaultIndex, docValueFields, indexName, eventId, @@ -113,7 +110,7 @@ export const useTimelineEventsDetails = ({ } return prevRequest; }); - }, [defaultIndex, docValueFields, eventId, indexName, skip]); + }, [docValueFields, eventId, indexName, skip]); useEffect(() => { if (timelineDetailsRequest) { diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx index d56a601fda4a38..54db52b985c314 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -10,18 +10,11 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import { ESQuery } from '../../../common/typed_json'; -import { - IIndexPattern, - isCompleteResponse, - isErrorResponse, -} from '../../../../../../src/plugins/data/public'; - -import { DEFAULT_INDEX_KEY } from '../../../common/constants'; +import { isCompleteResponse, isErrorResponse } from '../../../../../../src/plugins/data/public'; import { inputsModel } from '../../common/store'; import { useKibana } from '../../common/lib/kibana'; import { createFilter } from '../../common/containers/helpers'; import { DocValueFields } from '../../common/containers/query_template'; -import { EventType } from '../../timelines/store/timeline/model'; import { timelineActions } from '../../timelines/store/timeline'; import { detectionsTimelineIds, skipQueryForDetectionsPage } from './helpers'; import { getInspectResponse } from '../../helpers'; @@ -58,15 +51,12 @@ interface UseTimelineEventsProps { filterQuery?: ESQuery | string; skip?: boolean; endDate: string; - eventType?: EventType; id: string; fields: string[]; - indexPattern?: IIndexPattern; - indexToAdd?: string[]; + indexNames: string[]; limit: number; sort: SortField; startDate: string; - canQueryTimeline?: boolean; } const getTimelineEvents = (timelineEdges: TimelineEdges[]): TimelineItem[] => @@ -77,10 +67,8 @@ const ID = 'timelineEventsQuery'; export const useTimelineEvents = ({ docValueFields, endDate, - eventType = 'raw', id = ID, - indexPattern, - indexToAdd = [], + indexNames, fields, filterQuery, startDate, @@ -89,41 +77,37 @@ export const useTimelineEvents = ({ field: '@timestamp', direction: Direction.asc, }, - canQueryTimeline = true, + skip = false, }: UseTimelineEventsProps): [boolean, TimelineArgs] => { const dispatch = useDispatch(); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultKibanaIndex = uiSettings.get(DEFAULT_INDEX_KEY); - const defaultIndex = - indexPattern == null || (indexPattern != null && indexPattern.title === '') - ? [ - ...(['all', 'raw'].includes(eventType) ? defaultKibanaIndex : []), - ...(['all', 'alert', 'signal'].includes(eventType) ? indexToAdd : []), - ] - : indexPattern?.title.split(',') ?? []; const [loading, setLoading] = useState(false); const [activePage, setActivePage] = useState(0); - const [timelineRequest, setTimelineRequest] = useState({ - fields, - fieldRequested: fields, - filterQuery: createFilter(filterQuery), - id, - timerange: { - interval: '12h', - from: startDate, - to: endDate, - }, - pagination: { - activePage, - querySize: limit, - }, - sort, - defaultIndex, - docValueFields: docValueFields ?? [], - factoryQueryType: TimelineEventsQueries.all, - }); + const [timelineRequest, setTimelineRequest] = useState( + !skip + ? { + fields, + fieldRequested: fields, + filterQuery: createFilter(filterQuery), + id, + timerange: { + interval: '12h', + from: startDate, + to: endDate, + }, + pagination: { + activePage, + querySize: limit, + }, + sort, + defaultIndex: indexNames, + docValueFields: docValueFields ?? [], + factoryQueryType: TimelineEventsQueries.all, + } + : null + ); const clearSignalsState = useCallback(() => { if (id != null && detectionsTimelineIds.some((timelineId) => timelineId === id)) { @@ -158,7 +142,11 @@ export const useTimelineEvents = ({ }); const timelineSearch = useCallback( - (request: TimelineEventsAllRequestOptions) => { + (request: TimelineEventsAllRequestOptions | null) => { + if (request == null) { + return; + } + let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); @@ -215,14 +203,19 @@ export const useTimelineEvents = ({ ); useEffect(() => { - if (!canQueryTimeline || skipQueryForDetectionsPage(id, defaultIndex)) { + if (skip || skipQueryForDetectionsPage(id, indexNames) || indexNames.length === 0) { return; } setTimelineRequest((prevRequest) => { const myRequest = { - ...prevRequest, - defaultIndex, + ...(prevRequest ?? { + fields, + fieldRequested: fields, + id, + factoryQueryType: TimelineEventsQueries.all, + }), + defaultIndex: indexNames, docValueFields: docValueFields ?? [], filterQuery: createFilter(filterQuery), pagination: { @@ -237,8 +230,8 @@ export const useTimelineEvents = ({ sort, }; if ( - canQueryTimeline && - !skipQueryForDetectionsPage(id, defaultIndex) && + !skip && + !skipQueryForDetectionsPage(id, indexNames) && !deepEqual(prevRequest, myRequest) ) { return myRequest; @@ -246,16 +239,17 @@ export const useTimelineEvents = ({ return prevRequest; }); }, [ - defaultIndex, + indexNames, docValueFields, endDate, filterQuery, startDate, - canQueryTimeline, id, activePage, limit, sort, + skip, + fields, ]); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/timelines/containers/one/index.gql_query.ts b/x-pack/plugins/security_solution/public/timelines/containers/one/index.gql_query.ts index 5e50a7fb3313e7..b85fbc15ce3f35 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/one/index.gql_query.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/one/index.gql_query.ts @@ -107,6 +107,7 @@ export const oneTimelineQuery = gql` serializedQuery } } + indexNames notes { eventId note diff --git a/x-pack/plugins/security_solution/public/timelines/containers/persist.gql_query.ts b/x-pack/plugins/security_solution/public/timelines/containers/persist.gql_query.ts index c38aa67ccebb20..12d3e6bfd71725 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/persist.gql_query.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/persist.gql_query.ts @@ -95,6 +95,7 @@ export const persistTimelineMutation = gql` serializedQuery } } + indexNames title dateRange { start diff --git a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.test.tsx b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.test.tsx index f9097ddef64900..3c81aa8dac078a 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.test.tsx @@ -21,12 +21,12 @@ jest.mock('react-router-dom', () => { }; }); jest.mock('../../overview/components/events_by_dataset'); -jest.mock('../../common/containers/source', () => { - const originalModule = jest.requireActual('../../common/containers/source'); +jest.mock('../../common/containers/sourcerer', () => { + const originalModule = jest.requireActual('../../common/containers/sourcerer'); return { ...originalModule, - useWithSource: jest.fn().mockReturnValue({ + useSourcererScope: jest.fn().mockReturnValue({ indicesExist: true, }), }; diff --git a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx index 79d0f909c7d592..136240939e7a3f 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx @@ -15,16 +15,14 @@ import { WrapperPage } from '../../common/components/wrapper_page'; import { useKibana } from '../../common/lib/kibana'; import { SpyRoute } from '../../common/utils/route/spy_routes'; import { useApolloClient } from '../../common/utils/apollo_context'; -import { useWithSource } from '../../common/containers/source'; import { OverviewEmpty } from '../../overview/components/overview_empty'; - import { StatefulOpenTimeline } from '../components/open_timeline'; import { NEW_TEMPLATE_TIMELINE } from '../components/timeline/properties/translations'; import { NewTemplateTimeline } from '../components/timeline/properties/new_template_timeline'; import { NewTimeline } from '../components/timeline/properties/helpers'; - import * as i18n from './translations'; import { SecurityPageName } from '../../app/types'; +import { useSourcererScope } from '../../common/containers/sourcerer'; const TimelinesContainer = styled.div` width: 100%; @@ -38,7 +36,7 @@ export const TimelinesPageComponent: React.FC = () => { const onImportTimelineBtnClick = useCallback(() => { setImportDataModalToggle(true); }, [setImportDataModalToggle]); - const { indicesExist } = useWithSource(); + const { indicesExist } = useSourcererScope(); const apolloClient = useApolloClient(); const capabilitiesCanUserCRUD: boolean = !!useKibana().services.application.capabilities.siem @@ -49,7 +47,7 @@ export const TimelinesPageComponent: React.FC = () => { {indicesExist ? ( <> - + {capabilitiesCanUserCRUD && ( @@ -97,7 +95,7 @@ export const TimelinesPageComponent: React.FC = () => { ) : ( - + )} diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts index da9c363703d16b..472e82426468e0 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts @@ -15,9 +15,13 @@ import { } from '../../../timelines/components/timeline/data_providers/data_provider'; import { KueryFilterQuery, SerializedFilterQuery } from '../../../common/store/types'; -import { EventType, KqlMode, TimelineModel, ColumnHeaderOptions } from './model'; +import { KqlMode, TimelineModel, ColumnHeaderOptions } from './model'; import { TimelineNonEcsData } from '../../../../common/search_strategy/timeline'; -import { TimelineTypeLiteral, RowRendererId } from '../../../../common/types/timeline'; +import { + TimelineEventsType, + TimelineTypeLiteral, + RowRendererId, +} from '../../../../common/types/timeline'; import { InsertTimeline } from './types'; const actionCreator = actionCreatorFactory('x-pack/security_solution/local/timeline'); @@ -63,6 +67,7 @@ export const createTimeline = actionCreator<{ filters?: Filter[]; columns: ColumnHeaderOptions[]; itemsPerPage?: number; + indexNames: string[]; kqlQuery?: { filterQuery: SerializedFilterQuery | null; filterQueryDraft: KueryFilterQuery | null; @@ -264,7 +269,7 @@ export const clearEventsDeleted = actionCreator<{ id: string; }>('CLEAR_TIMELINE_EVENTS_DELETED'); -export const updateEventType = actionCreator<{ id: string; eventType: EventType }>( +export const updateEventType = actionCreator<{ id: string; eventType: TimelineEventsType }>( 'UPDATE_EVENT_TYPE' ); @@ -272,3 +277,8 @@ export const setExcludedRowRendererIds = actionCreator<{ id: string; excludedRowRendererIds: RowRendererId[]; }>('SET_TIMELINE_EXCLUDED_ROW_RENDERER_IDS'); + +export const updateIndexNames = actionCreator<{ + id: string; + indexNames: string[]; +}>('UPDATE_INDEXES_NAME'); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts index 7980f62cff1718..ce469c2bf57a28 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts @@ -27,6 +27,7 @@ export const timelineDefaults: SubsetTimelineModel & Pick { exists: { field: '@timestamp' }, } as Filter, ], + indexNames: [], isFavorite: false, isLive: false, isSelectAllChecked: false, @@ -272,6 +273,7 @@ describe('Epic Timeline', () => { script: null, }, ], + indexNames: [], kqlMode: 'filter', kqlQuery: { filterQuery: { diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts index ad849c3a995b33..cc8e856de1b160 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts @@ -66,6 +66,7 @@ import { updateRange, updateSort, upsertColumn, + updateIndexNames, updateTimeline, updateTitle, updateAutoSaveMsg, @@ -105,6 +106,7 @@ const timelineActionsType = [ updateDescription.type, updateEventType.type, updateKqlMode.type, + updateIndexNames.type, updateProviders.type, updateSort.type, updateTitle.type, @@ -339,6 +341,7 @@ const timelineInput: TimelineInput = { filters: null, kqlMode: null, kqlQuery: null, + indexNames: null, title: null, timelineType: TimelineType.default, templateTimelineVersion: null, diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx index 8c3f30c75c35b7..6507603d304447 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx @@ -84,18 +84,16 @@ describe('epicLocalStorage', () => { dataProviders: mockDataProviders, docValueFields: [], end: endDate, - eventType: 'raw' as TimelineComponentProps['eventType'], filters: [], + indexNames: [], indexPattern, - indexToAdd: [], isLive: false, - isLoadingSource: false, isSaving: false, itemsPerPage: 5, itemsPerPageOptions: [5, 10, 20], kqlMode: 'search' as TimelineComponentProps['kqlMode'], kqlQueryExpression: '', - loadingIndexName: false, + loadingSourcerer: false, onChangeItemsPerPage: jest.fn(), onClose: jest.fn(), onDataProviderEdited: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts index 1432e133244d02..fc178df86362b2 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts @@ -22,6 +22,7 @@ import { import { KueryFilterQuery, SerializedFilterQuery } from '../../../common/store/model'; import { TimelineNonEcsData } from '../../../../common/search_strategy/timeline'; import { + TimelineEventsType, TimelineTypeLiteral, TimelineType, RowRendererId, @@ -29,7 +30,7 @@ import { import { normalizeTimeRange } from '../../../common/components/url_state/normalize_time_range'; import { timelineDefaults } from './defaults'; -import { ColumnHeaderOptions, KqlMode, TimelineModel, EventType } from './model'; +import { ColumnHeaderOptions, KqlMode, TimelineModel } from './model'; import { TimelineById } from './types'; export const isNotNull = (value: T | null): value is T => value !== null; @@ -139,6 +140,7 @@ interface AddNewTimelineParams { filters?: Filter[]; id: string; itemsPerPage?: number; + indexNames: string[]; kqlQuery?: { filterQuery: SerializedFilterQuery | null; filterQueryDraft: KueryFilterQuery | null; @@ -159,6 +161,7 @@ export const addNewTimeline = ({ filters = timelineDefaults.filters, id, itemsPerPage = timelineDefaults.itemsPerPage, + indexNames, kqlQuery = { filterQuery: null, filterQueryDraft: null }, sort = timelineDefaults.sort, show = false, @@ -186,6 +189,7 @@ export const addNewTimeline = ({ excludedRowRendererIds, filters, itemsPerPage, + indexNames, kqlQuery, sort, show, @@ -667,7 +671,7 @@ export const updateTimelineTitle = ({ interface UpdateTimelineEventTypeParams { id: string; - eventType: EventType; + eventType: TimelineEventsType; timelineById: TimelineById; } diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts index 88ec9da1e0c4ec..ec4d37d3b70a25 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts @@ -12,6 +12,7 @@ import { PinnedEvent } from '../../../graphql/types'; import { TimelineNonEcsData } from '../../../../common/search_strategy/timeline'; import { KueryFilterQuery, SerializedFilterQuery } from '../../../common/store/types'; import type { + TimelineEventsType, TimelineType, TimelineStatus, RowRendererId, @@ -19,7 +20,6 @@ import type { export const DEFAULT_PAGE_COUNT = 2; // Eui Pager will not render unless this is a minimum of 2 pages export type KqlMode = 'filter' | 'search'; -export type EventType = 'all' | 'raw' | 'alert' | 'signal'; export type ColumnHeaderType = 'not-filtered' | 'text-filter'; @@ -52,7 +52,7 @@ export interface TimelineModel { /** A summary of the events and notes in this timeline */ description: string; /** Typoe of event you want to see in this timeline */ - eventType?: EventType; + eventType?: TimelineEventsType; /** A map of events in this timeline to the chronologically ordered notes (in this timeline) associated with the event */ eventIdToNoteIds: Record; /** A list of Ids of excluded Row Renderers */ @@ -66,6 +66,8 @@ export interface TimelineModel { highlightedDropAndProviderId: string; /** Uniquely identifies the timeline */ id: string; + /** TO DO sourcerer @X define this */ + indexNames: string[]; /** If selectAll checkbox in header is checked **/ isSelectAllChecked: boolean; /** Events to be rendered as loading **/ @@ -136,6 +138,7 @@ export type SubsetTimelineModel = Readonly< | 'graphEventId' | 'highlightedDropAndProviderId' | 'historyIds' + | 'indexNames' | 'isFavorite' | 'isLive' | 'isSelectAllChecked' diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts index 45bdbd09792764..c2f43625ab464a 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts @@ -4,9 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { set } from '@elastic/safer-lodash-set/fp'; import { cloneDeep } from 'lodash/fp'; - import { TimelineType, TimelineStatus } from '../../../../common/types/timeline'; import { @@ -45,77 +43,76 @@ import { updateTimelineTitle, upsertTimelineColumn, } from './helpers'; -import { ColumnHeaderOptions } from './model'; +import { ColumnHeaderOptions, TimelineModel } from './model'; import { timelineDefaults } from './defaults'; import { TimelineById } from './types'; jest.mock('../../../common/components/url_state/normalize_time_range.ts'); -const timelineByIdMock: TimelineById = { - foo: { - dataProviders: [ - { - and: [], - id: '123', - name: 'data provider 1', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', - }, - ], - columns: [], - description: '', - deletedEventIds: [], - eventIdToNoteIds: {}, - excludedRowRendererIds: [], - highlightedDropAndProviderId: '', - historyIds: [], - id: 'foo', - savedObjectId: null, - isFavorite: false, - isLive: false, - isSelectAllChecked: false, - isLoading: false, - itemsPerPage: 25, - itemsPerPageOptions: [10, 25, 50], - kqlMode: 'filter', - kqlQuery: { filterQuery: null, filterQueryDraft: null }, - loadingEventIds: [], - title: '', - timelineType: TimelineType.default, - templateTimelineVersion: null, - templateTimelineId: null, - noteIds: [], - pinnedEventIds: {}, - pinnedEventsSaveObject: {}, - dateRange: { - start: '2020-07-07T08:20:18.966Z', - end: '2020-07-08T08:20:18.966Z', - }, - selectedEventIds: {}, - show: true, - showCheckboxes: false, - sort: { - columnId: '@timestamp', - sortDirection: Direction.desc, - }, - status: TimelineStatus.active, - width: DEFAULT_TIMELINE_WIDTH, - isSaving: false, - version: null, +const basicDataProvider: DataProvider = { + and: [], + id: '123', + name: 'data provider 1', + enabled: true, + queryMatch: { + field: '', + value: '', + operator: IS_OPERATOR, }, + excluded: false, + kqlQuery: '', +}; +const basicTimeline: TimelineModel = { + columns: [], + dataProviders: [{ ...basicDataProvider }], + dateRange: { + start: '2020-07-07T08:20:18.966Z', + end: '2020-07-08T08:20:18.966Z', + }, + deletedEventIds: [], + description: '', + eventIdToNoteIds: {}, + excludedRowRendererIds: [], + highlightedDropAndProviderId: '', + historyIds: [], + id: 'foo', + indexNames: [], + isFavorite: false, + isLive: false, + isLoading: false, + isSaving: false, + isSelectAllChecked: false, + itemsPerPage: 25, + itemsPerPageOptions: [10, 25, 50], + kqlMode: 'filter', + kqlQuery: { filterQuery: null, filterQueryDraft: null }, + loadingEventIds: [], + noteIds: [], + pinnedEventIds: {}, + pinnedEventsSaveObject: {}, + savedObjectId: null, + selectedEventIds: {}, + show: true, + showCheckboxes: false, + sort: { + columnId: '@timestamp', + sortDirection: Direction.desc, + }, + status: TimelineStatus.active, + templateTimelineId: null, + templateTimelineVersion: null, + timelineType: TimelineType.default, + title: '', + version: null, + width: DEFAULT_TIMELINE_WIDTH, +}; +const timelineByIdMock: TimelineById = { + foo: { ...basicTimeline }, }; const timelineByIdTemplateMock: TimelineById = { - ...timelineByIdMock, foo: { - ...timelineByIdMock.foo, + ...basicTimeline, timelineType: TimelineType.template, }, }; @@ -132,14 +129,14 @@ describe('Timeline', () => { const update = addTimelineToStore({ id: 'foo', timeline: { - ...timelineByIdMock.foo, + ...basicTimeline, }, timelineById: timelineByIdMock, }); expect(update).toEqual({ foo: { - ...timelineByIdMock.foo, + ...basicTimeline, show: true, }, }); @@ -151,6 +148,7 @@ describe('Timeline', () => { const update = addNewTimeline({ id: 'bar', columns: defaultHeaders, + indexNames: [], timelineById: timelineByIdMock, timelineType: TimelineType.default, }); @@ -161,27 +159,29 @@ describe('Timeline', () => { const update = addNewTimeline({ id: 'bar', columns: timelineDefaults.columns, + indexNames: [], timelineById: timelineByIdMock, timelineType: TimelineType.default, }); expect(update).toEqual({ - foo: timelineByIdMock.foo, - bar: set('id', 'bar', timelineDefaults), + foo: basicTimeline, + bar: { ...timelineDefaults, id: 'bar' }, }); }); test('should add the specified columns to the timeline', () => { - const barWithEmptyColumns = set('id', 'bar', timelineDefaults); - const barWithPopulatedColumns = set('columns', defaultHeaders, barWithEmptyColumns); + const barWithEmptyColumns = { ...timelineDefaults, id: 'bar' }; + const barWithPopulatedColumns = { ...barWithEmptyColumns, columns: defaultHeaders }; const update = addNewTimeline({ id: 'bar', columns: defaultHeaders, + indexNames: [], timelineById: timelineByIdMock, timelineType: TimelineType.default, }); expect(update).toEqual({ - foo: timelineByIdMock.foo, + foo: basicTimeline, bar: barWithPopulatedColumns, }); }); @@ -203,7 +203,14 @@ describe('Timeline', () => { show: false, // value we are changing from true to false timelineById: timelineByIdMock, }); - expect(update).toEqual(set('foo.show', false, timelineByIdMock)); + + expect(update).toEqual({ + ...timelineByIdMock, + foo: { + ...timelineByIdMock.foo, + show: false, + }, + }); }); }); @@ -211,6 +218,7 @@ describe('Timeline', () => { let timelineById: TimelineById = {}; let columns: ColumnHeaderOptions[] = []; let columnToAdd: ColumnHeaderOptions; + let mockWithExistingColumns: TimelineById; beforeEach(() => { timelineById = cloneDeep(timelineByIdMock); @@ -226,6 +234,13 @@ describe('Timeline', () => { aggregatable: true, width: DEFAULT_COLUMN_MIN_WIDTH, }; + mockWithExistingColumns = { + ...timelineById, + foo: { + ...timelineById.foo, + columns, + }, + }; }); test('should return a new reference and not the same reference', () => { @@ -248,12 +263,11 @@ describe('Timeline', () => { timelineById, }); - expect(update).toEqual(set('foo.columns', expectedColumns, timelineById)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should add a new column to an existing collection of columns at the beginning of the collection', () => { const expectedColumns = [columnToAdd, ...columns]; - const mockWithExistingColumns = set('foo.columns', columns, timelineById); const update = upsertTimelineColumn({ column: columnToAdd, @@ -261,13 +275,11 @@ describe('Timeline', () => { index: 0, timelineById: mockWithExistingColumns, }); - - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should add a new column to an existing collection of columns in the middle of the collection', () => { const expectedColumns = [columns[0], columnToAdd, columns[1], columns[2]]; - const mockWithExistingColumns = set('foo.columns', columns, timelineById); const update = upsertTimelineColumn({ column: columnToAdd, @@ -276,12 +288,11 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should add a new column to an existing collection of columns at the end of the collection', () => { const expectedColumns = [...columns, columnToAdd]; - const mockWithExistingColumns = set('foo.columns', columns, timelineById); const update = upsertTimelineColumn({ column: columnToAdd, @@ -290,13 +301,11 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); columns.forEach((column, i) => { test(`should upsert (NOT add a new column) a column when already exists at the same index (${i})`, () => { - const mockWithExistingColumns = set('foo.columns', columns, timelineById); - const update = upsertTimelineColumn({ column, id: 'foo', @@ -304,13 +313,12 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', columns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(columns); }); }); test('should allow the 1st column to be moved to the 2nd column', () => { const expectedColumns = [columns[1], columns[0], columns[2]]; - const mockWithExistingColumns = set('foo.columns', columns, timelineById); const update = upsertTimelineColumn({ column: columns[0], @@ -319,12 +327,11 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should allow the 1st column to be moved to the 3rd column', () => { const expectedColumns = [columns[1], columns[2], columns[0]]; - const mockWithExistingColumns = set('foo.columns', columns, timelineById); const update = upsertTimelineColumn({ column: columns[0], @@ -333,12 +340,11 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should allow the 2nd column to be moved to the 1st column', () => { const expectedColumns = [columns[1], columns[0], columns[2]]; - const mockWithExistingColumns = set('foo.columns', columns, timelineById); const update = upsertTimelineColumn({ column: columns[1], @@ -347,12 +353,11 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should allow the 2nd column to be moved to the 3rd column', () => { const expectedColumns = [columns[0], columns[2], columns[1]]; - const mockWithExistingColumns = set('foo.columns', columns, timelineById); const update = upsertTimelineColumn({ column: columns[1], @@ -361,12 +366,11 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should allow the 3rd column to be moved to the 1st column', () => { const expectedColumns = [columns[2], columns[0], columns[1]]; - const mockWithExistingColumns = set('foo.columns', columns, timelineById); const update = upsertTimelineColumn({ column: columns[2], @@ -375,12 +379,11 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should allow the 3rd column to be moved to the 2nd column', () => { const expectedColumns = [columns[0], columns[2], columns[1]]; - const mockWithExistingColumns = set('foo.columns', columns, timelineById); const update = upsertTimelineColumn({ column: columns[2], @@ -389,75 +392,39 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); }); describe('#addTimelineProvider', () => { + const providerToAdd: DataProvider = { + ...basicDataProvider, + id: '567', + name: 'data provider 2', + }; test('should return a new reference and not the same reference', () => { const update = addTimelineProvider({ id: 'foo', - provider: { - and: [], - id: '567', - name: 'data provider 2', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', - }, + provider: providerToAdd, timelineById: timelineByIdMock, }); expect(update).not.toBe(timelineByIdMock); }); test('should add a new timeline provider', () => { - const providerToAdd: DataProvider = { - and: [], - id: '567', - name: 'data provider 2', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', - }; const update = addTimelineProvider({ id: 'foo', provider: providerToAdd, timelineById: timelineByIdMock, }); - const addedDataProvider = timelineByIdMock.foo.dataProviders.concat(providerToAdd); - expect(update).toEqual(set('foo.dataProviders', addedDataProvider, timelineByIdMock)); + const addedDataProvider = [...basicTimeline.dataProviders].concat(providerToAdd); + expect(update.foo.dataProviders).toEqual(addedDataProvider); }); test('should NOT add a new timeline provider if it already exists and the attributes "and" is empty', () => { - const providerToAdd: DataProvider = { - and: [], - id: '123', - name: 'data provider 1', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', - }; const update = addTimelineProvider({ id: 'foo', - provider: providerToAdd, + provider: basicDataProvider, timelineById: timelineByIdMock, }); expect(update).toEqual(timelineByIdMock); @@ -467,68 +434,45 @@ describe('Timeline', () => { const myMockTimelineByIdMock = cloneDeep(timelineByIdMock); myMockTimelineByIdMock.foo.dataProviders[0].and = [ { + ...basicDataProvider, id: '456', name: 'and data provider 1', - enabled: true, - excluded: false, - kqlQuery: '', - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, }, ]; - const providerToAdd: DataProvider = { - and: [], - id: '123', - name: 'data provider 1', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', - }; + const provider = { ...basicDataProvider }; const update = addTimelineProvider({ id: 'foo', - provider: providerToAdd, + provider, timelineById: myMockTimelineByIdMock, }); - expect(update).toEqual(set('foo.dataProviders[1]', providerToAdd, myMockTimelineByIdMock)); + expect(update.foo.dataProviders[1]).toEqual(provider); }); test('should UPSERT an existing timeline provider if it already exists', () => { - const providerToAdd: DataProvider = { - and: [], - id: '123', - name: 'my name changed', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - excluded: false, - kqlQuery: '', - }; const update = addTimelineProvider({ id: 'foo', - provider: providerToAdd, + provider: { + ...basicDataProvider, + name: 'my name changed', + }, timelineById: timelineByIdMock, }); - expect(update).toEqual(set('foo.dataProviders[0].name', 'my name changed', timelineByIdMock)); + expect(update.foo.dataProviders[0].name).toEqual('my name changed'); }); }); describe('#removeTimelineColumn', () => { + let mockWithExistingColumns: TimelineById; + beforeEach(() => { + mockWithExistingColumns = { + ...timelineByIdMock, + foo: { + ...timelineByIdMock.foo, + columns: columnsMock, + }, + }; + }); test('should return a new reference and not the same reference', () => { - // pre-populate a new mock with existing columns: - const mockWithExistingColumns = set('foo.columns', columnsMock, timelineByIdMock); - const update = removeTimelineColumn({ id: 'foo', columnId: columnsMock[0].id, @@ -541,70 +485,65 @@ describe('Timeline', () => { test('should remove just the first column when the id matches', () => { const expectedColumns = [columnsMock[1], columnsMock[2]]; - // pre-populate a new mock with existing columns: - const mockWithExistingColumns = set('foo.columns', columnsMock, timelineByIdMock); - const update = removeTimelineColumn({ id: 'foo', columnId: columnsMock[0].id, timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should remove just the last column when the id matches', () => { const expectedColumns = [columnsMock[0], columnsMock[1]]; - // pre-populate a new mock with existing columns: - const mockWithExistingColumns = set('foo.columns', columnsMock, timelineByIdMock); - const update = removeTimelineColumn({ id: 'foo', columnId: columnsMock[2].id, timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should remove just the middle column when the id matches', () => { const expectedColumns = [columnsMock[0], columnsMock[2]]; - // pre-populate a new mock with existing columns: - const mockWithExistingColumns = set('foo.columns', columnsMock, timelineByIdMock); - const update = removeTimelineColumn({ id: 'foo', columnId: columnsMock[1].id, timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should not modify the columns if the id to remove was not found', () => { const expectedColumns = cloneDeep(columnsMock); - // pre-populate a new mock with existing columns: - const mockWithExistingColumns = set('foo.columns', columnsMock, timelineByIdMock); - const update = removeTimelineColumn({ id: 'foo', columnId: 'does.not.exist', timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); }); describe('#applyDeltaToColumnWidth', () => { + let mockWithExistingColumns: TimelineById; + beforeEach(() => { + mockWithExistingColumns = { + ...timelineByIdMock, + foo: { + ...timelineByIdMock.foo, + columns: columnsMock, + }, + }; + }); test('should return a new reference and not the same reference', () => { const delta = 50; - // pre-populate a new mock with existing columns: - const mockWithExistingColumns = set('foo.columns', columnsMock, timelineByIdMock); - const update = applyDeltaToTimelineColumnWidth({ id: 'foo', columnId: columnsMock[0].id, @@ -624,9 +563,6 @@ describe('Timeline', () => { }; const expectedColumns = [expectedToHaveNewWidth, columnsMock[1], columnsMock[2]]; - // pre-populate a new mock with existing columns: - const mockWithExistingColumns = set('foo.columns', columnsMock, timelineByIdMock); - const update = applyDeltaToTimelineColumnWidth({ id: 'foo', columnId: aDateColumn.id, @@ -634,7 +570,7 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should NOT update (just) the specified column of type `date` when the id matches, because the result of applying the delta is less than the min width for a date column', () => { @@ -646,9 +582,6 @@ describe('Timeline', () => { }; const expectedColumns = [expectedToHaveNewWidth, columnsMock[1], columnsMock[2]]; - // pre-populate a new mock with existing columns: - const mockWithExistingColumns = set('foo.columns', columnsMock, timelineByIdMock); - const update = applyDeltaToTimelineColumnWidth({ id: 'foo', columnId: aDateColumn.id, @@ -656,7 +589,7 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should update (just) the specified non-date column when the id matches, and the result of applying the delta is greater than the min width for the column', () => { @@ -668,9 +601,6 @@ describe('Timeline', () => { }; const expectedColumns = [columnsMock[0], expectedToHaveNewWidth, columnsMock[2]]; - // pre-populate a new mock with existing columns: - const mockWithExistingColumns = set('foo.columns', columnsMock, timelineByIdMock); - const update = applyDeltaToTimelineColumnWidth({ id: 'foo', columnId: aNonDateColumn.id, @@ -678,7 +608,7 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); test('should NOT update the specified non-date column when the id matches, because the result of applying the delta is less than the min width for the column', () => { @@ -690,9 +620,6 @@ describe('Timeline', () => { }; const expectedColumns = [columnsMock[0], expectedToHaveNewWidth, columnsMock[2]]; - // pre-populate a new mock with existing columns: - const mockWithExistingColumns = set('foo.columns', columnsMock, timelineByIdMock); - const update = applyDeltaToTimelineColumnWidth({ id: 'foo', columnId: aNonDateColumn.id, @@ -700,24 +627,21 @@ describe('Timeline', () => { timelineById: mockWithExistingColumns, }); - expect(update).toEqual(set('foo.columns', expectedColumns, mockWithExistingColumns)); + expect(update.foo.columns).toEqual(expectedColumns); }); }); describe('#addAndProviderToTimelineProvider', () => { test('should add a new and provider to an existing timeline provider', () => { const providerToAdd: DataProvider = { - and: [], + ...basicDataProvider, id: '567', name: 'data provider 2', - enabled: true, queryMatch: { field: 'handsome', - value: 'garrett', + value: 'xavier', operator: IS_OPERATOR, }, - excluded: false, - kqlQuery: '', }; const newTimeline = addTimelineProvider({ @@ -729,18 +653,14 @@ describe('Timeline', () => { newTimeline.foo.highlightedDropAndProviderId = '567'; const andProviderToAdd: DataProvider = { - and: [], + ...basicDataProvider, id: '568', name: 'And Data Provider', - enabled: true, queryMatch: { field: 'smart', - value: 'frank', + value: 'steph', operator: IS_OPERATOR, }, - - excluded: false, - kqlQuery: '', }; const update = addTimelineProvider({ @@ -757,30 +677,25 @@ describe('Timeline', () => { test('should add another and provider because it is not a duplicate', () => { const providerToAdd: DataProvider = { + ...basicDataProvider, and: [ { + ...basicDataProvider, id: '568', name: 'And Data Provider', - enabled: true, queryMatch: { field: 'smart', - value: 'garrett', + value: 'xavier', operator: IS_OPERATOR, }, - excluded: false, - kqlQuery: '', }, ], id: '567', - name: 'data provider 1', - enabled: true, queryMatch: { field: 'handsome', - value: 'frank', + value: 'steph', operator: IS_OPERATOR, }, - excluded: false, - kqlQuery: '', }; const newTimeline = addTimelineProvider({ @@ -792,17 +707,14 @@ describe('Timeline', () => { newTimeline.foo.highlightedDropAndProviderId = '567'; const andProviderToAdd: DataProvider = { - and: [], + ...basicDataProvider, id: '569', name: 'And Data Provider', - enabled: true, queryMatch: { field: 'happy', value: 'andrewG', operator: IS_OPERATOR, }, - excluded: false, - kqlQuery: '', }; // temporary, we will have to decouple DataProvider & DataProvidersAnd // that's bigger a refactor than just fixing a bug @@ -814,36 +726,31 @@ describe('Timeline', () => { timelineById: newTimeline, }); - expect(update).toEqual(set('foo.dataProviders[1].and[1]', andProviderToAdd, newTimeline)); + expect(update.foo.dataProviders[1].and[1]).toEqual(andProviderToAdd); newTimeline.foo.highlightedDropAndProviderId = ''; }); test('should NOT add another and provider because it is a duplicate', () => { const providerToAdd: DataProvider = { + ...basicDataProvider, and: [ { + ...basicDataProvider, id: '568', name: 'And Data Provider', - enabled: true, queryMatch: { field: 'smart', - value: 'garrett', + value: 'xavier', operator: IS_OPERATOR, }, - excluded: false, - kqlQuery: '', }, ], id: '567', - name: 'data provider 1', - enabled: true, queryMatch: { field: 'handsome', - value: 'frank', + value: 'steph', operator: IS_OPERATOR, }, - excluded: false, - kqlQuery: '', }; const newTimeline = addTimelineProvider({ @@ -855,17 +762,14 @@ describe('Timeline', () => { newTimeline.foo.highlightedDropAndProviderId = '567'; const andProviderToAdd: DataProvider = { - and: [], + ...basicDataProvider, id: '569', name: 'And Data Provider', - enabled: true, queryMatch: { field: 'smart', - value: 'garrett', + value: 'xavier', operator: IS_OPERATOR, }, - excluded: false, - kqlQuery: '', }; const update = addTimelineProvider({ id: 'foo', @@ -894,7 +798,7 @@ describe('Timeline', () => { columns: columnsMock, timelineById: timelineByIdMock, }); - expect(update).toEqual(set('foo.columns', [...columnsMock], timelineByIdMock)); + expect(update.foo.columns).toEqual([...columnsMock]); }); }); @@ -916,7 +820,7 @@ describe('Timeline', () => { description: newDescription, timelineById: timelineByIdMock, }); - expect(update).toEqual(set('foo.description', newDescription, timelineByIdMock)); + expect(update.foo.description).toEqual(newDescription); }); test('should always trim all leading whitespace and allow only one trailing space', () => { @@ -925,7 +829,7 @@ describe('Timeline', () => { description: ' breathing room ', timelineById: timelineByIdMock, }); - expect(update).toEqual(set('foo.description', 'breathing room ', timelineByIdMock)); + expect(update.foo.description).toEqual('breathing room '); }); }); @@ -947,7 +851,7 @@ describe('Timeline', () => { title: newTitle, timelineById: timelineByIdMock, }); - expect(update).toEqual(set('foo.title', newTitle, timelineByIdMock)); + expect(update.foo.title).toEqual(newTitle); }); test('should always trim all leading whitespace and allow only one trailing space', () => { @@ -956,7 +860,7 @@ describe('Timeline', () => { title: ' room at the back ', timelineById: timelineByIdMock, }); - expect(update).toEqual(set('foo.title', 'room at the back ', timelineByIdMock)); + expect(update.foo.title).toEqual('room at the back '); }); }); @@ -966,18 +870,9 @@ describe('Timeline', () => { id: 'foo', providers: [ { - and: [], + ...basicDataProvider, id: '567', name: 'data provider 2', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }, ], timelineById: timelineByIdMock, @@ -985,64 +880,47 @@ describe('Timeline', () => { expect(update).not.toBe(timelineByIdMock); }); - test('should add update a timeline with new providers', () => { + test('should add update a timeline with new providers BBB', () => { const providerToAdd: DataProvider = { - and: [], + ...basicDataProvider, id: '567', name: 'data provider 2', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }; const update = updateTimelineProviders({ id: 'foo', providers: [providerToAdd], timelineById: timelineByIdMock, }); - expect(update).toEqual(set('foo.dataProviders', [providerToAdd], timelineByIdMock)); + expect(update.foo.dataProviders).toEqual([providerToAdd]); }); }); describe('#updateTimelineRange', () => { - test('should return a new reference and not the same reference', () => { - const update = updateTimelineRange({ + let update: TimelineById; + beforeAll(() => { + update = updateTimelineRange({ id: 'foo', start: '2020-07-07T08:20:18.966Z', end: '2020-07-08T08:20:18.966Z', timelineById: timelineByIdMock, }); + }); + test('should return a new reference and not the same reference', () => { expect(update).not.toBe(timelineByIdMock); }); test('should update the timeline range', () => { - const update = updateTimelineRange({ - id: 'foo', + expect(update.foo.dateRange).toEqual({ start: '2020-07-07T08:20:18.966Z', end: '2020-07-08T08:20:18.966Z', - timelineById: timelineByIdMock, }); - expect(update).toEqual( - set( - 'foo.dateRange', - { - start: '2020-07-07T08:20:18.966Z', - end: '2020-07-08T08:20:18.966Z', - }, - timelineByIdMock - ) - ); }); }); describe('#updateTimelineSort', () => { - test('should return a new reference and not the same reference', () => { - const update = updateTimelineSort({ + let update: TimelineById; + beforeAll(() => { + update = updateTimelineSort({ id: 'foo', sort: { columnId: 'some column', @@ -1050,31 +928,19 @@ describe('Timeline', () => { }, timelineById: timelineByIdMock, }); + }); + test('should return a new reference and not the same reference', () => { expect(update).not.toBe(timelineByIdMock); }); test('should update the timeline range', () => { - const update = updateTimelineSort({ - id: 'foo', - sort: { - columnId: 'some column', - sortDirection: Direction.desc, - }, - timelineById: timelineByIdMock, - }); - expect(update).toEqual( - set( - 'foo.sort', - { columnId: 'some column', sortDirection: Direction.desc }, - timelineByIdMock - ) - ); + expect(update.foo.sort).toEqual({ columnId: 'some column', sortDirection: Direction.desc }); }); }); describe('#updateTimelineProviderEnabled', () => { test('should return a new reference and not the same reference', () => { - const update = updateTimelineProviderEnabled({ + const update: TimelineById = updateTimelineProviderEnabled({ id: 'foo', providerId: '123', enabled: false, // value we are updating from true to false @@ -1084,17 +950,17 @@ describe('Timeline', () => { }); test('should return a new reference for data provider and not the same reference of data provider', () => { - const update = updateTimelineProviderEnabled({ + const update: TimelineById = updateTimelineProviderEnabled({ id: 'foo', providerId: '123', enabled: false, // value we are updating from true to false timelineById: timelineByIdMock, }); - expect(update.foo.dataProviders).not.toBe(timelineByIdMock.foo.dataProviders); + expect(update.foo.dataProviders).not.toBe(basicTimeline.dataProviders); }); test('should update the timeline provider enabled from true to false', () => { - const update = updateTimelineProviderEnabled({ + const update: TimelineById = updateTimelineProviderEnabled({ id: 'foo', providerId: '123', enabled: false, // value we are updating from true to false @@ -1102,81 +968,29 @@ describe('Timeline', () => { }); const expected: TimelineById = { foo: { - id: 'foo', - savedObjectId: null, - columns: [], + ...basicTimeline, dataProviders: [ { - and: [], - id: '123', - name: 'data provider 1', - enabled: false, // This value changed from true to false - excluded: false, - kqlQuery: '', - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, + ...basicDataProvider, + enabled: false, }, ], - deletedEventIds: [], - description: '', - eventIdToNoteIds: {}, - excludedRowRendererIds: [], - highlightedDropAndProviderId: '', - historyIds: [], - isFavorite: false, - isLive: false, - isSelectAllChecked: false, - isLoading: false, - kqlMode: 'filter', - kqlQuery: { filterQuery: null, filterQueryDraft: null }, - loadingEventIds: [], - title: '', - timelineType: TimelineType.default, - templateTimelineVersion: null, - templateTimelineId: null, - noteIds: [], - dateRange: { - start: '2020-07-07T08:20:18.966Z', - end: '2020-07-08T08:20:18.966Z', - }, - selectedEventIds: {}, - show: true, - showCheckboxes: false, - sort: { - columnId: '@timestamp', - sortDirection: Direction.desc, - }, - status: TimelineStatus.active, - pinnedEventIds: {}, - pinnedEventsSaveObject: {}, - itemsPerPage: 25, - itemsPerPageOptions: [10, 25, 50], - width: DEFAULT_TIMELINE_WIDTH, - isSaving: false, - version: null, }, }; expect(update).toEqual(expected); }); test('should update only one data provider and not two data providers', () => { - const multiDataProvider = timelineByIdMock.foo.dataProviders.concat({ - and: [], + const multiDataProvider = [...basicTimeline.dataProviders].concat({ + ...basicDataProvider, id: '456', - name: 'data provider 1', - enabled: true, - excluded: false, - kqlQuery: '', - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, }); - const multiDataProviderMock = set('foo.dataProviders', multiDataProvider, timelineByIdMock); + const multiDataProviderMock = { + foo: { + ...timelineByIdMock.foo, + dataProviders: multiDataProvider, + }, + }; const update = updateTimelineProviderEnabled({ id: 'foo', providerId: '123', @@ -1185,74 +999,17 @@ describe('Timeline', () => { }); const expected: TimelineById = { foo: { - id: 'foo', - savedObjectId: null, - columns: [], + ...basicTimeline, dataProviders: [ { - and: [], - id: '123', - name: 'data provider 1', - enabled: false, // value we are updating from true to false - excluded: false, - kqlQuery: '', - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, + ...basicDataProvider, + enabled: false, }, { - and: [], + ...basicDataProvider, id: '456', - name: 'data provider 1', - enabled: true, - excluded: false, - kqlQuery: '', - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, }, ], - description: '', - deletedEventIds: [], - eventIdToNoteIds: {}, - excludedRowRendererIds: [], - highlightedDropAndProviderId: '', - historyIds: [], - isFavorite: false, - isLive: false, - isSelectAllChecked: false, - isLoading: false, - kqlMode: 'filter', - kqlQuery: { filterQuery: null, filterQueryDraft: null }, - loadingEventIds: [], - title: '', - timelineType: TimelineType.default, - templateTimelineVersion: null, - templateTimelineId: null, - noteIds: [], - dateRange: { - start: '2020-07-07T08:20:18.966Z', - end: '2020-07-08T08:20:18.966Z', - }, - selectedEventIds: {}, - show: true, - showCheckboxes: false, - sort: { - columnId: '@timestamp', - sortDirection: Direction.desc, - }, - status: TimelineStatus.active, - pinnedEventIds: {}, - pinnedEventsSaveObject: {}, - itemsPerPage: 25, - itemsPerPageOptions: [10, 25, 50], - width: DEFAULT_TIMELINE_WIDTH, - isSaving: false, - version: null, }, }; expect(update).toEqual(expected); @@ -1261,34 +1018,18 @@ describe('Timeline', () => { describe('#updateTimelineAndProviderEnabled', () => { let timelineByIdwithAndMock: TimelineById = timelineByIdMock; + let update: TimelineById; beforeEach(() => { const providerToAdd: DataProvider = { + ...basicDataProvider, and: [ { + ...basicDataProvider, id: '568', name: 'And Data Provider', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }, ], id: '567', - name: 'data provider 1', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }; timelineByIdwithAndMock = addTimelineProvider({ @@ -1296,43 +1037,30 @@ describe('Timeline', () => { provider: providerToAdd, timelineById: timelineByIdMock, }); - }); - test('should return a new reference and not the same reference', () => { - const update = updateTimelineProviderEnabled({ + update = updateTimelineProviderEnabled({ id: 'foo', providerId: '567', enabled: false, // value we are updating from true to false timelineById: timelineByIdwithAndMock, andProviderId: '568', }); + }); + + test('should return a new reference and not the same reference', () => { expect(update).not.toBe(timelineByIdwithAndMock); }); test('should return a new reference for and data provider and not the same reference of data and provider', () => { - const update = updateTimelineProviderEnabled({ - id: 'foo', - providerId: '567', - enabled: false, // value we are updating from true to false - timelineById: timelineByIdwithAndMock, - andProviderId: '568', - }); - expect(update.foo.dataProviders).not.toBe(timelineByIdMock.foo.dataProviders); + expect(update.foo.dataProviders).not.toBe(basicTimeline.dataProviders); }); test('should update the timeline and provider enabled from true to false', () => { - const update = updateTimelineProviderEnabled({ - id: 'foo', - providerId: '567', - enabled: false, // value we are updating from true to false - timelineById: timelineByIdwithAndMock, - andProviderId: '568', - }); const indexProvider = update.foo.dataProviders.findIndex((i) => i.id === '567'); expect(update.foo.dataProviders[indexProvider].and[0].enabled).toEqual(false); }); - test('should update only one and data provider and not two and data providers', () => { + test('should update only one and data provider and not two and data providers ahhhh', () => { const indexProvider = timelineByIdwithAndMock.foo.dataProviders.findIndex( (i) => i.id === '567' ); @@ -1351,12 +1079,9 @@ describe('Timeline', () => { excluded: false, kqlQuery: '', }); - const multiAndDataProviderMock = set( - `foo.dataProviders[${indexProvider}].and`, - multiAndDataProvider, - timelineByIdwithAndMock - ); - const update = updateTimelineProviderEnabled({ + const multiAndDataProviderMock = timelineByIdwithAndMock; + multiAndDataProviderMock.foo.dataProviders[indexProvider].and = multiAndDataProvider; + update = updateTimelineProviderEnabled({ id: 'foo', providerId: '567', enabled: false, // value we are updating from true to false @@ -1375,111 +1100,51 @@ describe('Timeline', () => { }); describe('#updateTimelineProviderExcluded', () => { - test('should return a new reference and not the same reference', () => { - const update = updateTimelineProviderExcluded({ + let update: TimelineById; + beforeAll(() => { + update = updateTimelineProviderExcluded({ id: 'foo', providerId: '123', excluded: true, // value we are updating from false to true timelineById: timelineByIdMock, }); + }); + test('should return a new reference and not the same reference', () => { expect(update).not.toBe(timelineByIdMock); }); test('should return a new reference for data provider and not the same reference of data provider', () => { - const update = updateTimelineProviderExcluded({ - id: 'foo', - providerId: '123', - excluded: true, // value we are updating from false to true - timelineById: timelineByIdMock, - }); - expect(update.foo.dataProviders).not.toBe(timelineByIdMock.foo.dataProviders); + expect(update.foo.dataProviders).not.toBe(basicTimeline.dataProviders); }); test('should update the timeline provider excluded from true to false', () => { - const update = updateTimelineProviderExcluded({ - id: 'foo', - providerId: '123', - excluded: true, // value we are updating from false to true - timelineById: timelineByIdMock, - }); const expected: TimelineById = { foo: { - id: 'foo', - savedObjectId: null, - columns: [], + ...basicTimeline, dataProviders: [ { - and: [], - id: '123', - name: 'data provider 1', - enabled: true, - excluded: true, // This value changed from true to false - kqlQuery: '', - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, + ...basicDataProvider, + excluded: true, }, ], - description: '', - deletedEventIds: [], - eventIdToNoteIds: {}, - excludedRowRendererIds: [], - highlightedDropAndProviderId: '', - historyIds: [], - isFavorite: false, - isLive: false, - isSelectAllChecked: false, - isLoading: false, - kqlMode: 'filter', - kqlQuery: { filterQuery: null, filterQueryDraft: null }, - loadingEventIds: [], - title: '', - timelineType: TimelineType.default, - templateTimelineVersion: null, - templateTimelineId: null, - noteIds: [], - dateRange: { - start: '2020-07-07T08:20:18.966Z', - end: '2020-07-08T08:20:18.966Z', - }, - selectedEventIds: {}, - show: true, - showCheckboxes: false, - sort: { - columnId: '@timestamp', - sortDirection: Direction.desc, - }, - status: TimelineStatus.active, - pinnedEventIds: {}, - pinnedEventsSaveObject: {}, - itemsPerPage: 25, - itemsPerPageOptions: [10, 25, 50], - width: DEFAULT_TIMELINE_WIDTH, - isSaving: false, - version: null, }, }; expect(update).toEqual(expected); }); test('should update only one data provider and not two data providers', () => { - const multiDataProvider = timelineByIdMock.foo.dataProviders.concat({ - and: [], + const multiDataProvider = basicTimeline.dataProviders.concat({ + ...basicDataProvider, id: '456', - name: 'data provider 1', - enabled: true, - excluded: false, - kqlQuery: '', - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, }); - const multiDataProviderMock = set('foo.dataProviders', multiDataProvider, timelineByIdMock); - const update = updateTimelineProviderExcluded({ + const multiDataProviderMock = { + ...timelineByIdMock, + foo: { + ...timelineByIdMock.foo, + dataProviders: multiDataProvider, + }, + }; + update = updateTimelineProviderExcluded({ id: 'foo', providerId: '123', excluded: true, // value we are updating from false to true @@ -1487,74 +1152,17 @@ describe('Timeline', () => { }); const expected: TimelineById = { foo: { - id: 'foo', - savedObjectId: null, - columns: [], + ...basicTimeline, dataProviders: [ { - and: [], - id: '123', - name: 'data provider 1', - enabled: true, + ...basicDataProvider, excluded: true, // value we are updating from false to true - kqlQuery: '', - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, }, { - and: [], + ...basicDataProvider, id: '456', - name: 'data provider 1', - enabled: true, - excluded: false, - kqlQuery: '', - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, }, ], - description: '', - deletedEventIds: [], - eventIdToNoteIds: {}, - excludedRowRendererIds: [], - highlightedDropAndProviderId: '', - historyIds: [], - isFavorite: false, - isLive: false, - isSelectAllChecked: false, - isLoading: false, - kqlMode: 'filter', - kqlQuery: { filterQuery: null, filterQueryDraft: null }, - loadingEventIds: [], - title: '', - timelineType: TimelineType.default, - templateTimelineId: null, - templateTimelineVersion: null, - noteIds: [], - dateRange: { - start: '2020-07-07T08:20:18.966Z', - end: '2020-07-08T08:20:18.966Z', - }, - selectedEventIds: {}, - show: true, - showCheckboxes: false, - sort: { - columnId: '@timestamp', - sortDirection: Direction.desc, - }, - status: TimelineStatus.active, - pinnedEventIds: {}, - pinnedEventsSaveObject: {}, - itemsPerPage: 25, - itemsPerPageOptions: [10, 25, 50], - width: DEFAULT_TIMELINE_WIDTH, - isSaving: false, - version: null, }, }; expect(update).toEqual(expected); @@ -1596,173 +1204,71 @@ describe('Timeline', () => { const update = updateTimelineProviderType({ id: 'foo', providerId: '123', - type: DataProviderType.template, // value we are updating from default to template + type: DataProviderType.template, timelineById: timelineByIdTemplateMock, }); const expected: TimelineById = { foo: { - id: 'foo', - savedObjectId: null, - columns: [], + ...basicTimeline, dataProviders: [ { - and: [], - id: '123', - name: '', // This value changed - enabled: true, - excluded: false, - kqlQuery: '', - type: DataProviderType.template, // value we are updating from default to template + ...basicDataProvider, + name: '', queryMatch: { field: '', - value: '{}', // This value changed + value: '{}', operator: IS_OPERATOR, }, + type: DataProviderType.template, }, ], - description: '', - deletedEventIds: [], - eventIdToNoteIds: {}, - excludedRowRendererIds: [], - highlightedDropAndProviderId: '', - historyIds: [], - isFavorite: false, - isLive: false, - isSelectAllChecked: false, - isLoading: false, - kqlMode: 'filter', - kqlQuery: { filterQuery: null, filterQueryDraft: null }, - loadingEventIds: [], - title: '', timelineType: TimelineType.template, - templateTimelineVersion: null, - templateTimelineId: null, - noteIds: [], - dateRange: { - start: '2020-07-07T08:20:18.966Z', - end: '2020-07-08T08:20:18.966Z', - }, - selectedEventIds: {}, - show: true, - showCheckboxes: false, - sort: { - columnId: '@timestamp', - sortDirection: Direction.desc, - }, - status: TimelineStatus.active, - pinnedEventIds: {}, - pinnedEventsSaveObject: {}, - itemsPerPage: 25, - itemsPerPageOptions: [10, 25, 50], - width: DEFAULT_TIMELINE_WIDTH, - isSaving: false, - version: null, }, }; + expect(update).toEqual(expected); }); + test('should update only one data provider and not two data providers AHH', () => { + const multiDataProvider = [ + ...timelineByIdTemplateMock.foo.dataProviders, + { + ...basicDataProvider, + id: '456', + type: DataProviderType.template, + }, + ]; - test('should update only one data provider and not two data providers', () => { - const multiDataProvider = timelineByIdTemplateMock.foo.dataProviders.concat({ - and: [], - id: '456', - name: 'data provider 1', - enabled: true, - excluded: false, - type: DataProviderType.template, - kqlQuery: '', - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, + const multiDataProviderMock = { + ...timelineByIdTemplateMock, + foo: { + ...timelineByIdTemplateMock.foo, + dataProviders: multiDataProvider, }, - }); - const multiDataProviderMock = set( - 'foo.dataProviders', - multiDataProvider, - timelineByIdTemplateMock - ); + }; const update = updateTimelineProviderType({ id: 'foo', providerId: '123', type: DataProviderType.template, // value we are updating from default to template timelineById: multiDataProviderMock, }); - const expected: TimelineById = { - foo: { - id: 'foo', - savedObjectId: null, - columns: [], - dataProviders: [ - { - and: [], - id: '123', - name: '', - enabled: true, - excluded: false, - type: DataProviderType.template, // value we are updating from default to template - kqlQuery: '', - queryMatch: { - field: '', - value: '{}', - operator: IS_OPERATOR, - }, - }, - { - and: [], - id: '456', - name: 'data provider 1', - enabled: true, - excluded: false, - kqlQuery: '', - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - type: DataProviderType.template, - }, - ], - description: '', - deletedEventIds: [], - eventIdToNoteIds: {}, - excludedRowRendererIds: [], - highlightedDropAndProviderId: '', - historyIds: [], - isFavorite: false, - isLive: false, - isSelectAllChecked: false, - isLoading: false, - kqlMode: 'filter', - kqlQuery: { filterQuery: null, filterQueryDraft: null }, - loadingEventIds: [], - title: '', - timelineType: TimelineType.template, - templateTimelineId: null, - templateTimelineVersion: null, - noteIds: [], - dateRange: { - start: '2020-07-07T08:20:18.966Z', - end: '2020-07-08T08:20:18.966Z', - }, - selectedEventIds: {}, - show: true, - showCheckboxes: false, - sort: { - columnId: '@timestamp', - sortDirection: Direction.desc, + const expected = [ + { + ...basicDataProvider, + name: '', + type: DataProviderType.template, + queryMatch: { + field: '', + value: '{}', + operator: IS_OPERATOR, }, - status: TimelineStatus.active, - pinnedEventIds: {}, - pinnedEventsSaveObject: {}, - itemsPerPage: 25, - itemsPerPageOptions: [10, 25, 50], - width: DEFAULT_TIMELINE_WIDTH, - isSaving: false, - version: null, }, - }; - expect(update).toEqual(expected); + { + ...basicDataProvider, + id: '456', + type: DataProviderType.template, + }, + ]; + expect(update.foo.dataProviders).toEqual(expected); }); }); @@ -1770,32 +1276,15 @@ describe('Timeline', () => { let timelineByIdwithAndMock: TimelineById = timelineByIdMock; beforeEach(() => { const providerToAdd: DataProvider = { + ...basicDataProvider, and: [ { + ...basicDataProvider, id: '568', name: 'And Data Provider', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }, ], id: '567', - name: 'data provider 1', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }; timelineByIdwithAndMock = addTimelineProvider({ @@ -1824,7 +1313,7 @@ describe('Timeline', () => { timelineById: timelineByIdwithAndMock, andProviderId: '568', }); - expect(update.foo.dataProviders).not.toBe(timelineByIdMock.foo.dataProviders); + expect(update.foo.dataProviders).not.toBe(basicTimeline.dataProviders); }); test('should update the timeline and provider excluded from true to false', () => { @@ -1846,23 +1335,12 @@ describe('Timeline', () => { const multiAndDataProvider = timelineByIdwithAndMock.foo.dataProviders[ indexProvider ].and.concat({ + ...basicDataProvider, id: '456', name: 'new and data provider', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }); - const multiAndDataProviderMock = set( - `foo.dataProviders[${indexProvider}].and`, - multiAndDataProvider, - timelineByIdwithAndMock - ); + const multiAndDataProviderMock = timelineByIdwithAndMock; + multiAndDataProviderMock.foo.dataProviders[indexProvider].and = multiAndDataProvider; const update = updateTimelineProviderExcluded({ id: 'foo', providerId: '567', @@ -1899,62 +1377,8 @@ describe('Timeline', () => { }); const expected: TimelineById = { foo: { - id: 'foo', - savedObjectId: null, - columns: [], - dataProviders: [ - { - and: [], - id: '123', - name: 'data provider 1', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', - }, - ], - description: '', - deletedEventIds: [], - eventIdToNoteIds: {}, - excludedRowRendererIds: [], - highlightedDropAndProviderId: '', - historyIds: [], - isFavorite: false, - isLive: false, - isSelectAllChecked: false, - isLoading: false, - kqlMode: 'filter', - kqlQuery: { filterQuery: null, filterQueryDraft: null }, - loadingEventIds: [], - title: '', - timelineType: TimelineType.default, - templateTimelineVersion: null, - templateTimelineId: null, - noteIds: [], - dateRange: { - start: '2020-07-07T08:20:18.966Z', - end: '2020-07-08T08:20:18.966Z', - }, - selectedEventIds: {}, - show: true, - showCheckboxes: false, - sort: { - columnId: '@timestamp', - sortDirection: Direction.desc, - }, - status: TimelineStatus.active, - pinnedEventIds: {}, - pinnedEventsSaveObject: {}, + ...basicTimeline, itemsPerPage: 50, - itemsPerPageOptions: [10, 25, 50], - width: DEFAULT_TIMELINE_WIDTH, - isSaving: false, - version: null, }, }; expect(update).toEqual(expected); @@ -1979,62 +1403,8 @@ describe('Timeline', () => { }); const expected: TimelineById = { foo: { - columns: [], - dataProviders: [ - { - and: [], - id: '123', - name: 'data provider 1', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', - }, - ], - description: '', - deletedEventIds: [], - eventIdToNoteIds: {}, - excludedRowRendererIds: [], - highlightedDropAndProviderId: '', - historyIds: [], - isFavorite: false, - isLive: false, - isSelectAllChecked: false, - isLoading: false, - id: 'foo', - savedObjectId: null, - kqlMode: 'filter', - kqlQuery: { filterQuery: null, filterQueryDraft: null }, - loadingEventIds: [], - title: '', - timelineType: TimelineType.default, - templateTimelineVersion: null, - templateTimelineId: null, - noteIds: [], - dateRange: { - start: '2020-07-07T08:20:18.966Z', - end: '2020-07-08T08:20:18.966Z', - }, - selectedEventIds: {}, - show: true, - showCheckboxes: false, - sort: { - columnId: '@timestamp', - sortDirection: Direction.desc, - }, - status: TimelineStatus.active, - pinnedEventIds: {}, - pinnedEventsSaveObject: {}, - itemsPerPage: 25, + ...basicTimeline, itemsPerPageOptions: [100, 200, 300], // updated - width: DEFAULT_TIMELINE_WIDTH, - isSaving: false, - version: null, }, }; expect(update).toEqual(expected); @@ -2057,25 +1427,22 @@ describe('Timeline', () => { providerId: '123', timelineById: timelineByIdMock, }); - expect(update).toEqual(set('foo.dataProviders', [], timelineByIdMock)); + expect(update.foo.dataProviders).toEqual([]); }); test('should remove only one data provider and not two data providers', () => { - const multiDataProvider = timelineByIdMock.foo.dataProviders.concat({ - and: [], + const multiDataProvider = basicTimeline.dataProviders.concat({ + ...basicDataProvider, id: '456', name: 'data provider 2', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }); - const multiDataProviderMock = set('foo.dataProviders', multiDataProvider, timelineByIdMock); + const multiDataProviderMock = { + ...timelineByIdMock, + foo: { + ...timelineByIdMock.foo, + dataProviders: multiDataProvider, + }, + }; const update = removeTimelineProvider({ id: 'foo', providerId: '123', @@ -2083,62 +1450,14 @@ describe('Timeline', () => { }); const expected: TimelineById = { foo: { - columns: [], + ...basicTimeline, dataProviders: [ { - and: [], + ...basicDataProvider, id: '456', name: 'data provider 2', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }, ], - description: '', - deletedEventIds: [], - eventIdToNoteIds: {}, - excludedRowRendererIds: [], - highlightedDropAndProviderId: '', - historyIds: [], - id: 'foo', - savedObjectId: null, - isFavorite: false, - isLive: false, - isSelectAllChecked: false, - isLoading: false, - kqlMode: 'filter', - kqlQuery: { filterQuery: null, filterQueryDraft: null }, - loadingEventIds: [], - title: '', - timelineType: TimelineType.default, - templateTimelineVersion: null, - templateTimelineId: null, - noteIds: [], - dateRange: { - start: '2020-07-07T08:20:18.966Z', - end: '2020-07-08T08:20:18.966Z', - }, - selectedEventIds: {}, - show: true, - showCheckboxes: false, - sort: { - columnId: '@timestamp', - sortDirection: Direction.desc, - }, - status: TimelineStatus.active, - pinnedEventIds: {}, - pinnedEventsSaveObject: {}, - itemsPerPage: 25, - itemsPerPageOptions: [10, 25, 50], - width: DEFAULT_TIMELINE_WIDTH, - isSaving: false, - version: null, }, }; expect(update).toEqual(expected); @@ -2147,99 +1466,58 @@ describe('Timeline', () => { test('should remove only first provider and not nested andProvider', () => { const dataProviders: DataProvider[] = [ { - and: [], + ...basicDataProvider, id: '111', - name: 'data provider 1', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }, { - and: [], + ...basicDataProvider, id: '222', name: 'data provider 2', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }, { - and: [], + ...basicDataProvider, id: '333', name: 'data provider 3', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }, ]; - const multiDataProviderMock = set('foo.dataProviders', dataProviders, timelineByIdMock); - + const multiDataProviderMock = { + ...timelineByIdMock, + foo: { + ...timelineByIdMock.foo, + dataProviders, + }, + }; const andDataProvider: DataProvidersAnd = { + ...basicDataProvider, id: '211', name: 'And Data Provider', - enabled: true, - queryMatch: { - field: '', - value: '', - operator: IS_OPERATOR, - }, - - excluded: false, - kqlQuery: '', }; - const nestedMultiAndDataProviderMock = set( - 'foo.dataProviders[1].and', - [andDataProvider], - multiDataProviderMock - ); + const nestedMultiAndDataProviderMock = multiDataProviderMock; + multiDataProviderMock.foo.dataProviders[1].and = [andDataProvider]; const update = removeTimelineProvider({ id: 'foo', providerId: '222', timelineById: nestedMultiAndDataProviderMock, }); - expect(update).toEqual( - set( - 'foo.dataProviders', - [ - nestedMultiAndDataProviderMock.foo.dataProviders[0], - { ...andDataProvider, and: [] }, - nestedMultiAndDataProviderMock.foo.dataProviders[2], - ], - timelineByIdMock - ) - ); + expect(update.foo.dataProviders).toEqual([ + nestedMultiAndDataProviderMock.foo.dataProviders[0], + { ...andDataProvider, and: [] }, + nestedMultiAndDataProviderMock.foo.dataProviders[2], + ]); }); test('should remove only the first provider and keep multiple nested andProviders', () => { const multiDataProvider: DataProvider[] = [ { + ...basicDataProvider, and: [ { - enabled: true, + ...basicDataProvider, id: 'socket_closed-MSoH7GoB9v5HJNSHRYj1-user_name-root', name: 'root', - excluded: false, - kqlQuery: '', queryMatch: { field: 'user.name', value: 'root', @@ -2247,11 +1525,9 @@ describe('Timeline', () => { }, }, { - enabled: true, + ...basicDataProvider, id: 'executed-yioH7GoB9v5HJNSHKnp5-auditd_result-success', name: 'success', - excluded: false, - kqlQuery: '', queryMatch: { field: 'auditd.result', value: 'success', @@ -2259,11 +1535,8 @@ describe('Timeline', () => { }, }, ], - enabled: true, - excluded: false, id: 'hosts-table-hostName-suricata-iowa', name: 'suricata-iowa', - kqlQuery: '', queryMatch: { field: 'host.name', value: 'suricata-iowa', @@ -2272,7 +1545,13 @@ describe('Timeline', () => { }, ]; - const multiDataProviderMock = set('foo.dataProviders', multiDataProvider, timelineByIdMock); + const multiDataProviderMock = { + ...timelineByIdMock, + foo: { + ...timelineByIdMock.foo, + dataProviders: multiDataProvider, + }, + }; const update = removeTimelineProvider({ id: 'foo', @@ -2280,51 +1559,40 @@ describe('Timeline', () => { timelineById: multiDataProviderMock, }); - expect(update).toEqual( - set( - 'foo.dataProviders', - [ + expect(update.foo.dataProviders).toEqual([ + { + ...basicDataProvider, + id: 'socket_closed-MSoH7GoB9v5HJNSHRYj1-user_name-root', + name: 'root', + queryMatch: { + field: 'user.name', + value: 'root', + operator: ':', + }, + and: [ { - enabled: true, - id: 'socket_closed-MSoH7GoB9v5HJNSHRYj1-user_name-root', - name: 'root', - excluded: false, - kqlQuery: '', + ...basicDataProvider, + id: 'executed-yioH7GoB9v5HJNSHKnp5-auditd_result-success', + name: 'success', queryMatch: { - field: 'user.name', - value: 'root', + field: 'auditd.result', + value: 'success', operator: ':', }, - and: [ - { - enabled: true, - id: 'executed-yioH7GoB9v5HJNSHKnp5-auditd_result-success', - name: 'success', - excluded: false, - kqlQuery: '', - queryMatch: { - field: 'auditd.result', - value: 'success', - operator: ':', - }, - }, - ], }, ], - timelineByIdMock - ) - ); + }, + ]); }); test('should remove only the first AND provider when the first AND is deleted, and there are multiple andProviders', () => { const multiDataProvider: DataProvider[] = [ { + ...basicDataProvider, and: [ { - enabled: true, + ...basicDataProvider, id: 'socket_closed-MSoH7GoB9v5HJNSHRYj1-user_name-root', name: 'root', - excluded: false, - kqlQuery: '', queryMatch: { field: 'user.name', value: 'root', @@ -2332,11 +1600,9 @@ describe('Timeline', () => { }, }, { - enabled: true, + ...basicDataProvider, id: 'executed-yioH7GoB9v5HJNSHKnp5-auditd_result-success', name: 'success', - excluded: false, - kqlQuery: '', queryMatch: { field: 'auditd.result', value: 'success', @@ -2344,11 +1610,8 @@ describe('Timeline', () => { }, }, ], - enabled: true, - excluded: false, id: 'hosts-table-hostName-suricata-iowa', name: 'suricata-iowa', - kqlQuery: '', queryMatch: { field: 'host.name', value: 'suricata-iowa', @@ -2357,7 +1620,13 @@ describe('Timeline', () => { }, ]; - const multiDataProviderMock = set('foo.dataProviders', multiDataProvider, timelineByIdMock); + const multiDataProviderMock = { + ...timelineByIdMock, + foo: { + ...timelineByIdMock.foo, + dataProviders: multiDataProvider, + }, + }; const update = removeTimelineProvider({ andProviderId: 'socket_closed-MSoH7GoB9v5HJNSHRYj1-user_name-root', @@ -2366,52 +1635,41 @@ describe('Timeline', () => { timelineById: multiDataProviderMock, }); - expect(update).toEqual( - set( - 'foo.dataProviders', - [ + expect(update.foo.dataProviders).toEqual([ + { + ...basicDataProvider, + and: [ { - and: [ - { - enabled: true, - id: 'executed-yioH7GoB9v5HJNSHKnp5-auditd_result-success', - name: 'success', - excluded: false, - kqlQuery: '', - queryMatch: { - field: 'auditd.result', - value: 'success', - operator: ':', - }, - }, - ], - enabled: true, - excluded: false, - id: 'hosts-table-hostName-suricata-iowa', - name: 'suricata-iowa', - kqlQuery: '', + ...basicDataProvider, + id: 'executed-yioH7GoB9v5HJNSHKnp5-auditd_result-success', + name: 'success', queryMatch: { - field: 'host.name', - value: 'suricata-iowa', + field: 'auditd.result', + value: 'success', operator: ':', }, }, ], - timelineByIdMock - ) - ); + id: 'hosts-table-hostName-suricata-iowa', + name: 'suricata-iowa', + queryMatch: { + field: 'host.name', + value: 'suricata-iowa', + operator: ':', + }, + }, + ]); }); test('should remove only the second AND provider when the second AND is deleted, and there are multiple andProviders', () => { const multiDataProvider: DataProvider[] = [ { + ...basicDataProvider, and: [ { - enabled: true, + ...basicDataProvider, id: 'socket_closed-MSoH7GoB9v5HJNSHRYj1-user_name-root', name: 'root', - excluded: false, - kqlQuery: '', queryMatch: { field: 'user.name', value: 'root', @@ -2419,11 +1677,9 @@ describe('Timeline', () => { }, }, { - enabled: true, + ...basicDataProvider, id: 'executed-yioH7GoB9v5HJNSHKnp5-auditd_result-success', name: 'success', - excluded: false, - kqlQuery: '', queryMatch: { field: 'auditd.result', value: 'success', @@ -2431,11 +1687,8 @@ describe('Timeline', () => { }, }, ], - enabled: true, - excluded: false, id: 'hosts-table-hostName-suricata-iowa', name: 'suricata-iowa', - kqlQuery: '', queryMatch: { field: 'host.name', value: 'suricata-iowa', @@ -2444,7 +1697,13 @@ describe('Timeline', () => { }, ]; - const multiDataProviderMock = set('foo.dataProviders', multiDataProvider, timelineByIdMock); + const multiDataProviderMock = { + ...timelineByIdMock, + foo: { + ...timelineByIdMock.foo, + dataProviders: multiDataProvider, + }, + }; const update = removeTimelineProvider({ andProviderId: 'executed-yioH7GoB9v5HJNSHKnp5-auditd_result-success', @@ -2453,40 +1712,30 @@ describe('Timeline', () => { timelineById: multiDataProviderMock, }); - expect(update).toEqual( - set( - 'foo.dataProviders', - [ + expect(update.foo.dataProviders).toEqual([ + { + ...basicDataProvider, + and: [ { - and: [ - { - enabled: true, - id: 'socket_closed-MSoH7GoB9v5HJNSHRYj1-user_name-root', - name: 'root', - excluded: false, - kqlQuery: '', - queryMatch: { - field: 'user.name', - value: 'root', - operator: ':', - }, - }, - ], - enabled: true, - excluded: false, - id: 'hosts-table-hostName-suricata-iowa', - name: 'suricata-iowa', - kqlQuery: '', + ...basicDataProvider, + id: 'socket_closed-MSoH7GoB9v5HJNSHRYj1-user_name-root', + name: 'root', queryMatch: { - field: 'host.name', - value: 'suricata-iowa', + field: 'user.name', + value: 'root', operator: ':', }, }, ], - timelineByIdMock - ) - ); + id: 'hosts-table-hostName-suricata-iowa', + name: 'suricata-iowa', + queryMatch: { + field: 'host.name', + value: 'suricata-iowa', + operator: ':', + }, + }, + ]); }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts index d15bce5e217fa4..1d956e02e7083c 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts @@ -44,6 +44,7 @@ import { updateDescription, updateEventType, updateHighlightedDropAndProviderId, + updateIndexNames, updateIsFavorite, updateIsLive, updateIsLoading, @@ -135,6 +136,7 @@ export const timelineReducer = reducerWithInitialState(initialTimelineState) show, columns, itemsPerPage, + indexNames, kqlQuery, sort, showCheckboxes, @@ -152,6 +154,7 @@ export const timelineReducer = reducerWithInitialState(initialTimelineState) filters, id, itemsPerPage, + indexNames, kqlQuery, sort, show, @@ -521,4 +524,14 @@ export const timelineReducer = reducerWithInitialState(initialTimelineState) ...state, insertTimeline, })) + .case(updateIndexNames, (state, { id, indexNames }) => ({ + ...state, + timelineById: { + ...state.timelineById, + [id]: { + ...state.timelineById[id], + indexNames, + }, + }, + })) .build(); diff --git a/x-pack/plugins/security_solution/scripts/beat_docs/build.js b/x-pack/plugins/security_solution/scripts/beat_docs/build.js new file mode 100644 index 00000000000000..9b3607593a5db2 --- /dev/null +++ b/x-pack/plugins/security_solution/scripts/beat_docs/build.js @@ -0,0 +1,233 @@ +/* + * 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. + */ + +require('../../../../../src/setup_node_env'); + +// eslint-disable-next-line import/no-extraneous-dependencies +const extract = require('extract-zip'); +const fs = require('fs'); +// eslint-disable-next-line import/no-extraneous-dependencies +const yaml = require('js-yaml'); +const https = require('https'); +// eslint-disable-next-line import/no-extraneous-dependencies +const { get, isArray, isEmpty, isNumber, isString, pick } = require('lodash'); +// eslint-disable-next-line import/no-extraneous-dependencies +const Q = require('q'); +// eslint-disable-next-line import/no-extraneous-dependencies +const rimraf = require('rimraf'); +const { resolve } = require('path'); +// eslint-disable-next-line import/no-extraneous-dependencies +const tar = require('tar'); +const zlib = require('zlib'); + +const OUTPUT_DIRECTORY = resolve('scripts', 'beat_docs'); +const OUTPUT_SERVER_DIRECTORY = resolve('server', 'utils', 'beat_schema'); + +const beats = [ + { + filePath: `${OUTPUT_DIRECTORY}/auditbeat-7.9.0-darwin-x86_64.tar.gz`, + index: 'auditbeat-*', + outputDir: `${OUTPUT_DIRECTORY}/auditbeat-7.9.0-darwin-x86_64`, + url: + 'https://artifacts.elastic.co/downloads/beats/auditbeat/auditbeat-7.9.0-darwin-x86_64.tar.gz', + }, + { + filePath: `${OUTPUT_DIRECTORY}/filebeat-7.9.0-darwin-x86_64.tar.gz`, + index: 'filebeat-*', + outputDir: `${OUTPUT_DIRECTORY}/filebeat-7.9.0-darwin-x86_64`, + url: + 'https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.9.0-darwin-x86_64.tar.gz', + }, + { + filePath: `${OUTPUT_DIRECTORY}/packetbeat-7.9.0-darwin-x86_64.tar.gz`, + index: 'packetbeat-*', + outputDir: `${OUTPUT_DIRECTORY}/packetbeat-7.9.0-darwin-x86_64`, + url: + 'https://artifacts.elastic.co/downloads/beats/packetbeat/packetbeat-7.9.0-darwin-x86_64.tar.gz', + }, + { + filePath: `${OUTPUT_DIRECTORY}/winlogbeat-7.9.0-windows-x86_64.zip`, + index: 'winlogbeat-*', + outputDir: `${OUTPUT_DIRECTORY}`, + url: + 'https://artifacts.elastic.co/downloads/beats/winlogbeat/winlogbeat-7.9.0-windows-x86_64.zip', + }, +]; + +const download = async (url, filepath) => { + const fileStream = fs.createWriteStream(filepath); + const deferred = Q.defer(); + + fileStream + .on('open', function () { + https.get(url, function (res) { + res.on('error', function (err) { + deferred.reject(err); + }); + + res.pipe(fileStream); + }); + }) + .on('error', function (err) { + deferred.reject(err); + }) + .on('finish', function () { + deferred.resolve(filepath); + }); + + return deferred.promise; +}; + +const paramsToPick = ['category', 'description', 'example', 'name', 'type', 'format']; + +const onlyStringOrNumber = (fields) => + Object.keys(fields).reduce((acc, item) => { + let value = get(fields, item); + if (item === 'description' && isString(value)) { + value = value.replace(/\n/g, ' '); + } + return { + ...acc, + [item]: isString(value) || isNumber(value) ? value : JSON.stringify(value), + }; + }, {}); + +const convertFieldsToHash = (schemaFields, beatFields, path) => + schemaFields.fields && isArray(schemaFields.fields) + ? schemaFields.fields.reduce((accumulator, item) => { + if (item.name) { + const attr = isEmpty(path) ? item.name : `${path}.${item.name}`; + const splitAttr = attr.split('.'); + const category = splitAttr.length === 1 ? 'base' : splitAttr[0]; + const myItem = { + ...item, + category, + name: attr, + }; + if (!isEmpty(item.fields)) { + return { + ...accumulator, + ...convertFieldsToHash(myItem, beatFields, attr), + }; + } else if (beatFields[attr] === undefined) { + return { + ...accumulator, + [attr]: onlyStringOrNumber(pick(myItem, paramsToPick)), + }; + } + } + return accumulator; + }, {}) + : {}; + +const convertSchemaToHash = (schema, beatFields) => { + return schema.reduce((accumulator, item) => { + if (item.fields != null && !isEmpty(item.fields)) { + return { + ...accumulator, + ...convertFieldsToHash(item, accumulator), + }; + } + return accumulator; + }, beatFields); +}; + +const manageZipFields = async (beat, filePath, beatFields) => + new Promise((resolve, reject) => { + extract(filePath, { dir: beat.outputDir }, (err) => { + if (err) { + return reject(new Error(err)); + } + console.log('building fields', beat.index); + const obj = yaml.load( + fs.readFileSync(`${beat.outputDir}/winlogbeat-7.9.0-windows-x86_64/fields.yml`, { + encoding: 'utf-8', + }) + ); + const eBeatFields = convertSchemaToHash(obj, beatFields); + console.log('deleting files', beat.index); + rimraf.sync(`${beat.outputDir}/winlogbeat-7.9.0-windows-x86_64`); + rimraf.sync(beat.filePath); + resolve(eBeatFields); + }); + }); + +const manageTarFields = async (beat, filePath, beatFields) => + new Promise((resolve, reject) => { + fs.createReadStream(filePath) + .pipe(zlib.createGunzip()) + .pipe( + tar.extract({ + sync: true, + cwd: OUTPUT_DIRECTORY, + filter: function (path) { + return path.includes('fields.yml'); + }, + }) + ) + .on('end', function (err) { + if (err) { + return reject(new Error(err)); + } + console.log('building fields', beat.index); + const obj = yaml.load( + fs.readFileSync(`${beat.outputDir}/fields.yml`, { encoding: 'utf-8' }) + ); + const ebeatFields = convertSchemaToHash(obj, beatFields); + console.log('deleting files', beat.index); + rimraf.sync(beat.outputDir); + rimraf.sync(beat.filePath); + resolve(ebeatFields); + }); + }); + +async function main() { + let beatFields = { + _id: { + category: 'base', + description: 'Each document has an _id that uniquely identifies it', + example: 'Y-6TfmcB0WOhS6qyMv3s', + name: '_id', + type: 'keyword', + }, + _index: { + category: 'base', + description: + 'An index is like a ‘database’ in a relational database. It has a mapping which defines multiple types. An index is a logical namespace which maps to one or more primary shards and can have zero or more replica shards.', + example: 'auditbeat-8.0.0-2019.02.19-000001', + name: '_index', + type: 'keyword', + }, + }; + + for (const myBeat of beats) { + console.log('downloading', myBeat.index); + const filepath = await download(myBeat.url, myBeat.filePath); + if (myBeat.index === 'winlogbeat-*') { + beatFields = await manageZipFields(myBeat, filepath, beatFields); + } else { + beatFields = await manageTarFields(myBeat, filepath, beatFields); + } + console.log('done for', myBeat.index); + } + const body = `/* + * 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 { BeatFields } from '../../../common/search_strategy/security_solution/beat_fields'; + + /* eslint-disable @typescript-eslint/naming-convention */ + export const fieldsBeat: BeatFields = + ${JSON.stringify(beatFields, null, 2)}; + `; + fs.writeFileSync(`${OUTPUT_SERVER_DIRECTORY}/fields.ts`, body, 'utf-8'); +} + +if (require.main === module) { + main(); +} diff --git a/x-pack/plugins/security_solution/server/graphql/source_status/schema.gql.ts b/x-pack/plugins/security_solution/server/graphql/source_status/schema.gql.ts index 3062113f1b6351..60dc563a3e8d2c 100644 --- a/x-pack/plugins/security_solution/server/graphql/source_status/schema.gql.ts +++ b/x-pack/plugins/security_solution/server/graphql/source_status/schema.gql.ts @@ -37,6 +37,6 @@ export const sourceStatusSchema = gql` "Whether the configured alias or wildcard pattern resolve to any auditbeat indices" indicesExist(defaultIndex: [String!]!): Boolean! "The list of fields defined in the index mappings" - indexFields(defaultIndex: [String!]!): [IndexField!]! + indexFields(defaultIndex: [String!]!): [String!]! } `; diff --git a/x-pack/plugins/security_solution/server/graphql/timeline/schema.gql.ts b/x-pack/plugins/security_solution/server/graphql/timeline/schema.gql.ts index 573539e1bb54f6..70596a1b41ea09 100644 --- a/x-pack/plugins/security_solution/server/graphql/timeline/schema.gql.ts +++ b/x-pack/plugins/security_solution/server/graphql/timeline/schema.gql.ts @@ -167,6 +167,7 @@ export const timelineSchema = gql` filters: [FilterTimelineInput!] kqlMode: String kqlQuery: SerializedFilterQueryInput + indexNames: [String!] title: String templateTimelineId: String templateTimelineVersion: Int @@ -269,6 +270,7 @@ export const timelineSchema = gql` filters: [FilterTimelineResult!] kqlMode: String kqlQuery: SerializedFilterQueryResult + indexNames: [String!] notes: [NoteResult!] noteIds: [String!] pinnedEventIds: [String!] diff --git a/x-pack/plugins/security_solution/server/graphql/types.ts b/x-pack/plugins/security_solution/server/graphql/types.ts index 5887feb63c2a1b..4c85c08e137fa9 100644 --- a/x-pack/plugins/security_solution/server/graphql/types.ts +++ b/x-pack/plugins/security_solution/server/graphql/types.ts @@ -134,6 +134,8 @@ export interface TimelineInput { kqlQuery?: Maybe; + indexNames?: Maybe; + title?: Maybe; templateTimelineId?: Maybe; @@ -415,10 +417,6 @@ export enum FlowDirection { biDirectional = 'biDirectional', } -export type ToStringArrayNoNullable = any; - -export type ToIFieldSubTypeNonNullable = any; - export type ToStringArray = string[] | string; export type Date = string; @@ -433,6 +431,10 @@ export type ToAny = any; export type EsValue = any; +export type ToStringArrayNoNullable = any; + +export type ToIFieldSubTypeNonNullable = any; + // ==================================================== // Scalars // ==================================================== @@ -591,33 +593,7 @@ export interface SourceStatus { /** Whether the configured alias or wildcard pattern resolve to any auditbeat indices */ indicesExist: boolean; /** The list of fields defined in the index mappings */ - indexFields: IndexField[]; -} - -/** A descriptor of a field in an index */ -export interface IndexField { - /** Where the field belong */ - category: string; - /** Example of field's value */ - example?: Maybe; - /** whether the field's belong to an alias index */ - indexes: (Maybe)[]; - /** The name of the field */ - name: string; - /** The type of the field's values as recognized by Kibana */ - type: string; - /** Whether the field's values can be efficiently searched for */ - searchable: boolean; - /** Whether the field's values can be aggregated */ - aggregatable: boolean; - /** Description of the field */ - description?: Maybe; - - format?: Maybe; - /** the elastic type as mapped in the index */ - esTypes?: Maybe; - - subType?: Maybe; + indexFields: string[]; } export interface AuthenticationsData { @@ -1948,6 +1924,8 @@ export interface TimelineResult { kqlQuery?: Maybe; + indexNames?: Maybe; + notes?: Maybe; noteIds?: Maybe; @@ -2220,6 +2198,32 @@ export interface HostFields { type?: Maybe; } +/** A descriptor of a field in an index */ +export interface IndexField { + /** Where the field belong */ + category: string; + /** Example of field's value */ + example?: Maybe; + /** whether the field's belong to an alias index */ + indexes: (Maybe)[]; + /** The name of the field */ + name: string; + /** The type of the field's values as recognized by Kibana */ + type: string; + /** Whether the field's values can be efficiently searched for */ + searchable: boolean; + /** Whether the field's values can be aggregated */ + aggregatable: boolean; + /** Description of the field */ + description?: Maybe; + + format?: Maybe; + /** the elastic type as mapped in the index */ + esTypes?: Maybe; + + subType?: Maybe; +} + // ==================================================== // Arguments // ==================================================== @@ -3397,7 +3401,7 @@ export namespace SourceStatusResolvers { /** Whether the configured alias or wildcard pattern resolve to any auditbeat indices */ indicesExist?: IndicesExistResolver; /** The list of fields defined in the index mappings */ - indexFields?: IndexFieldsResolver; + indexFields?: IndexFieldsResolver; } export type IndicesExistResolver< @@ -3410,7 +3414,7 @@ export namespace SourceStatusResolvers { } export type IndexFieldsResolver< - R = IndexField[], + R = string[], Parent = SourceStatus, TContext = SiemContext > = Resolver; @@ -3418,89 +3422,6 @@ export namespace SourceStatusResolvers { defaultIndex: string[]; } } -/** A descriptor of a field in an index */ -export namespace IndexFieldResolvers { - export interface Resolvers { - /** Where the field belong */ - category?: CategoryResolver; - /** Example of field's value */ - example?: ExampleResolver, TypeParent, TContext>; - /** whether the field's belong to an alias index */ - indexes?: IndexesResolver<(Maybe)[], TypeParent, TContext>; - /** The name of the field */ - name?: NameResolver; - /** The type of the field's values as recognized by Kibana */ - type?: TypeResolver; - /** Whether the field's values can be efficiently searched for */ - searchable?: SearchableResolver; - /** Whether the field's values can be aggregated */ - aggregatable?: AggregatableResolver; - /** Description of the field */ - description?: DescriptionResolver, TypeParent, TContext>; - - format?: FormatResolver, TypeParent, TContext>; - /** the elastic type as mapped in the index */ - esTypes?: EsTypesResolver, TypeParent, TContext>; - - subType?: SubTypeResolver, TypeParent, TContext>; - } - - export type CategoryResolver = Resolver< - R, - Parent, - TContext - >; - export type ExampleResolver< - R = Maybe, - Parent = IndexField, - TContext = SiemContext - > = Resolver; - export type IndexesResolver< - R = (Maybe)[], - Parent = IndexField, - TContext = SiemContext - > = Resolver; - export type NameResolver = Resolver< - R, - Parent, - TContext - >; - export type TypeResolver = Resolver< - R, - Parent, - TContext - >; - export type SearchableResolver< - R = boolean, - Parent = IndexField, - TContext = SiemContext - > = Resolver; - export type AggregatableResolver< - R = boolean, - Parent = IndexField, - TContext = SiemContext - > = Resolver; - export type DescriptionResolver< - R = Maybe, - Parent = IndexField, - TContext = SiemContext - > = Resolver; - export type FormatResolver< - R = Maybe, - Parent = IndexField, - TContext = SiemContext - > = Resolver; - export type EsTypesResolver< - R = Maybe, - Parent = IndexField, - TContext = SiemContext - > = Resolver; - export type SubTypeResolver< - R = Maybe, - Parent = IndexField, - TContext = SiemContext - > = Resolver; -} export namespace AuthenticationsDataResolvers { export interface Resolvers { @@ -7925,6 +7846,8 @@ export namespace TimelineResultResolvers { kqlQuery?: KqlQueryResolver, TypeParent, TContext>; + indexNames?: IndexNamesResolver, TypeParent, TContext>; + notes?: NotesResolver, TypeParent, TContext>; noteIds?: NoteIdsResolver, TypeParent, TContext>; @@ -8025,6 +7948,11 @@ export namespace TimelineResultResolvers { Parent = TimelineResult, TContext = SiemContext > = Resolver; + export type IndexNamesResolver< + R = Maybe, + Parent = TimelineResult, + TContext = SiemContext + > = Resolver; export type NotesResolver< R = Maybe, Parent = TimelineResult, @@ -8973,6 +8901,89 @@ export namespace HostFieldsResolvers { TContext = SiemContext > = Resolver; } +/** A descriptor of a field in an index */ +export namespace IndexFieldResolvers { + export interface Resolvers { + /** Where the field belong */ + category?: CategoryResolver; + /** Example of field's value */ + example?: ExampleResolver, TypeParent, TContext>; + /** whether the field's belong to an alias index */ + indexes?: IndexesResolver<(Maybe)[], TypeParent, TContext>; + /** The name of the field */ + name?: NameResolver; + /** The type of the field's values as recognized by Kibana */ + type?: TypeResolver; + /** Whether the field's values can be efficiently searched for */ + searchable?: SearchableResolver; + /** Whether the field's values can be aggregated */ + aggregatable?: AggregatableResolver; + /** Description of the field */ + description?: DescriptionResolver, TypeParent, TContext>; + + format?: FormatResolver, TypeParent, TContext>; + /** the elastic type as mapped in the index */ + esTypes?: EsTypesResolver, TypeParent, TContext>; + + subType?: SubTypeResolver, TypeParent, TContext>; + } + + export type CategoryResolver = Resolver< + R, + Parent, + TContext + >; + export type ExampleResolver< + R = Maybe, + Parent = IndexField, + TContext = SiemContext + > = Resolver; + export type IndexesResolver< + R = (Maybe)[], + Parent = IndexField, + TContext = SiemContext + > = Resolver; + export type NameResolver = Resolver< + R, + Parent, + TContext + >; + export type TypeResolver = Resolver< + R, + Parent, + TContext + >; + export type SearchableResolver< + R = boolean, + Parent = IndexField, + TContext = SiemContext + > = Resolver; + export type AggregatableResolver< + R = boolean, + Parent = IndexField, + TContext = SiemContext + > = Resolver; + export type DescriptionResolver< + R = Maybe, + Parent = IndexField, + TContext = SiemContext + > = Resolver; + export type FormatResolver< + R = Maybe, + Parent = IndexField, + TContext = SiemContext + > = Resolver; + export type EsTypesResolver< + R = Maybe, + Parent = IndexField, + TContext = SiemContext + > = Resolver; + export type SubTypeResolver< + R = Maybe, + Parent = IndexField, + TContext = SiemContext + > = Resolver; +} /** Directs the executor to skip this field or fragment when the `if` argument is true. */ export type SkipDirectiveResolver = DirectiveResolverFn< @@ -9007,14 +9018,6 @@ export interface DeprecatedDirectiveArgs { reason?: string; } -export interface ToStringArrayNoNullableScalarConfig - extends GraphQLScalarTypeConfig { - name: 'ToStringArrayNoNullable'; -} -export interface ToIFieldSubTypeNonNullableScalarConfig - extends GraphQLScalarTypeConfig { - name: 'ToIFieldSubTypeNonNullable'; -} export interface ToStringArrayScalarConfig extends GraphQLScalarTypeConfig { name: 'ToStringArray'; } @@ -9036,6 +9039,14 @@ export interface ToAnyScalarConfig extends GraphQLScalarTypeConfig { export interface EsValueScalarConfig extends GraphQLScalarTypeConfig { name: 'EsValue'; } +export interface ToStringArrayNoNullableScalarConfig + extends GraphQLScalarTypeConfig { + name: 'ToStringArrayNoNullable'; +} +export interface ToIFieldSubTypeNonNullableScalarConfig + extends GraphQLScalarTypeConfig { + name: 'ToIFieldSubTypeNonNullable'; +} export type IResolvers = { Query?: QueryResolvers.Resolvers; @@ -9046,7 +9057,6 @@ export type IResolvers = { SourceConfiguration?: SourceConfigurationResolvers.Resolvers; SourceFields?: SourceFieldsResolvers.Resolvers; SourceStatus?: SourceStatusResolvers.Resolvers; - IndexField?: IndexFieldResolvers.Resolvers; AuthenticationsData?: AuthenticationsDataResolvers.Resolvers; AuthenticationsEdges?: AuthenticationsEdgesResolvers.Resolvers; AuthenticationItem?: AuthenticationItemResolvers.Resolvers; @@ -9182,8 +9192,7 @@ export type IResolvers = { EventsTimelineData?: EventsTimelineDataResolvers.Resolvers; OsFields?: OsFieldsResolvers.Resolvers; HostFields?: HostFieldsResolvers.Resolvers; - ToStringArrayNoNullable?: GraphQLScalarType; - ToIFieldSubTypeNonNullable?: GraphQLScalarType; + IndexField?: IndexFieldResolvers.Resolvers; ToStringArray?: GraphQLScalarType; Date?: GraphQLScalarType; ToNumberArray?: GraphQLScalarType; @@ -9191,6 +9200,8 @@ export type IResolvers = { ToBooleanArray?: GraphQLScalarType; ToAny?: GraphQLScalarType; EsValue?: GraphQLScalarType; + ToStringArrayNoNullable?: GraphQLScalarType; + ToIFieldSubTypeNonNullable?: GraphQLScalarType; } & { [typeName: string]: never }; export type IDirectiveResolvers = { diff --git a/x-pack/plugins/security_solution/server/lib/compose/kibana.ts b/x-pack/plugins/security_solution/server/lib/compose/kibana.ts index 430ada93b45149..cfd7bfbf255f60 100644 --- a/x-pack/plugins/security_solution/server/lib/compose/kibana.ts +++ b/x-pack/plugins/security_solution/server/lib/compose/kibana.ts @@ -45,7 +45,7 @@ export function compose( const domainLibs: AppDomainLibs = { authentications: new Authentications(new ElasticsearchAuthenticationAdapter(framework)), events: new Events(new ElasticsearchEventsAdapter(framework)), - fields: new IndexFields(new ElasticsearchIndexFieldAdapter(framework)), + fields: new IndexFields(new ElasticsearchIndexFieldAdapter()), hosts: new Hosts(new ElasticsearchHostsAdapter(framework, endpointContext)), ipDetails: new IpDetails(new ElasticsearchIpDetailsAdapter(framework)), kpiHosts: new KpiHosts(new ElasticsearchKpiHostsAdapter(framework)), diff --git a/x-pack/plugins/security_solution/server/lib/events/elasticsearch_adapter.ts b/x-pack/plugins/security_solution/server/lib/events/elasticsearch_adapter.ts index dda52e26ca42b8..8b656272ecc996 100644 --- a/x-pack/plugins/security_solution/server/lib/events/elasticsearch_adapter.ts +++ b/x-pack/plugins/security_solution/server/lib/events/elasticsearch_adapter.ts @@ -26,7 +26,6 @@ import { TimelineDetailsData, TimelineEdges, } from '../../graphql/types'; -import { baseCategoryFields } from '../../utils/beat_schema/8.0.0'; import { reduceFields } from '../../utils/build_query/reduce_fields'; import { mergeFieldsWithHit, inspectStringifyObject } from '../../utils/build_query'; import { eventFieldsMap } from '../ecs_fields'; @@ -44,6 +43,8 @@ import { TimelineRequestOptions, } from './types'; +const baseCategoryFields = ['@timestamp', 'labels', 'message', 'tags']; + export class ElasticsearchEventsAdapter implements EventsAdapter { constructor(private readonly framework: FrameworkAdapter) {} diff --git a/x-pack/plugins/security_solution/server/lib/index_fields/elasticsearch_adapter.ts b/x-pack/plugins/security_solution/server/lib/index_fields/elasticsearch_adapter.ts index 777b1cf3bb80d7..6cfa13bfd2a7ad 100644 --- a/x-pack/plugins/security_solution/server/lib/index_fields/elasticsearch_adapter.ts +++ b/x-pack/plugins/security_solution/server/lib/index_fields/elasticsearch_adapter.ts @@ -4,167 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { isEmpty } from 'lodash/fp'; - -import { IndexField } from '../../graphql/types'; -import { baseCategoryFields, getDocumentation, hasDocumentation } from '../../utils/beat_schema'; -import { FrameworkAdapter, FrameworkRequest } from '../framework'; -import { FieldsAdapter, IndexFieldDescriptor } from './types'; +import { FrameworkRequest } from '../framework'; +import { FieldsAdapter } from './types'; export class ElasticsearchIndexFieldAdapter implements FieldsAdapter { - constructor(private readonly framework: FrameworkAdapter) {} - public async getIndexFields(request: FrameworkRequest, indices: string[]): Promise { - const indexPatternsService = this.framework.getIndexPatternsService(request); - const responsesIndexFields = await Promise.all( - indices.map((index) => { - return indexPatternsService.getFieldsForWildcard({ - pattern: index, - }); - }) - ); - return formatIndexFields(responsesIndexFields, indices); + // Deprecated until we delete all the code + public async getIndexFields(request: FrameworkRequest, indices: string[]): Promise { + return Promise.resolve(['deprecated']); } } - -const missingFields = [ - { - name: '_id', - type: 'string', - searchable: true, - aggregatable: false, - readFromDocValues: true, - }, - { - name: '_index', - type: 'string', - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, -]; - -/** - * Creates a single field item. - * - * This is a mutatious HOT CODE PATH function that will have array sizes up to 4.7 megs - * in size at a time calling this function repeatedly. This function should be as optimized as possible - * and should avoid any and all creation of new arrays, iterating over the arrays or performing - * any n^2 operations. - * @param indexesAlias The index alias - * @param index The index its self - * @param indexesAliasIdx The index within the alias - */ -export const createFieldItem = ( - indexesAlias: string[], - index: IndexFieldDescriptor, - indexesAliasIdx: number -): IndexField => { - const alias = indexesAlias[indexesAliasIdx]; - const splitName = index.name.split('.'); - const category = baseCategoryFields.includes(splitName[0]) ? 'base' : splitName[0]; - return { - ...(hasDocumentation(alias, index.name) ? getDocumentation(alias, index.name) : {}), - ...index, - category, - indexes: [alias], - }; -}; - -/** - * This is a mutatious HOT CODE PATH function that will have array sizes up to 4.7 megs - * in size at a time when being called. This function should be as optimized as possible - * and should avoid any and all creation of new arrays, iterating over the arrays or performing - * any n^2 operations. The `.push`, and `forEach` operations are expected within this function - * to speed up performance. - * - * This intentionally waits for the next tick on the event loop to process as the large 4.7 megs - * has already consumed a lot of the event loop processing up to this function and we want to give - * I/O opportunity to occur by scheduling this on the next loop. - * @param responsesIndexFields The response index fields to loop over - * @param indexesAlias The index aliases such as filebeat-* - */ -export const formatFirstFields = async ( - responsesIndexFields: IndexFieldDescriptor[][], - indexesAlias: string[] -): Promise => { - return new Promise((resolve) => { - setTimeout(() => { - resolve( - responsesIndexFields.reduce( - ( - accumulator: IndexField[], - indexFields: IndexFieldDescriptor[], - indexesAliasIdx: number - ) => { - missingFields.forEach((index) => { - const item = createFieldItem(indexesAlias, index, indexesAliasIdx); - accumulator.push(item); - }); - indexFields.forEach((index) => { - const item = createFieldItem(indexesAlias, index, indexesAliasIdx); - accumulator.push(item); - }); - return accumulator; - }, - [] - ) - ); - }); - }); -}; - -/** - * This is a mutatious HOT CODE PATH function that will have array sizes up to 4.7 megs - * in size at a time when being called. This function should be as optimized as possible - * and should avoid any and all creation of new arrays, iterating over the arrays or performing - * any n^2 operations. The `.push`, and `forEach` operations are expected within this function - * to speed up performance. The "indexFieldNameHash" side effect hash avoids additional expensive n^2 - * look ups. - * - * This intentionally waits for the next tick on the event loop to process as the large 4.7 megs - * has already consumed a lot of the event loop processing up to this function and we want to give - * I/O opportunity to occur by scheduling this on the next loop. - * @param fields The index fields to create the secondary fields for - */ -export const formatSecondFields = async (fields: IndexField[]): Promise => { - return new Promise((resolve) => { - setTimeout(() => { - const indexFieldNameHash: Record = {}; - const reduced = fields.reduce((accumulator: IndexField[], indexfield: IndexField) => { - const alreadyExistingIndexField = indexFieldNameHash[indexfield.name]; - if (alreadyExistingIndexField != null) { - const existingIndexField = accumulator[alreadyExistingIndexField]; - if (isEmpty(accumulator[alreadyExistingIndexField].description)) { - accumulator[alreadyExistingIndexField].description = indexfield.description; - } - accumulator[alreadyExistingIndexField].indexes = Array.from( - new Set([...existingIndexField.indexes, ...indexfield.indexes]) - ); - return accumulator; - } - accumulator.push(indexfield); - indexFieldNameHash[indexfield.name] = accumulator.length - 1; - return accumulator; - }, []); - resolve(reduced); - }); - }); -}; - -/** - * Formats the index fields into a format the UI wants. - * - * NOTE: This will have array sizes up to 4.7 megs in size at a time when being called. - * This function should be as optimized as possible and should avoid any and all creation - * of new arrays, iterating over the arrays or performing any n^2 operations. - * @param responsesIndexFields The response index fields to format - * @param indexesAlias The index alias - */ -export const formatIndexFields = async ( - responsesIndexFields: IndexFieldDescriptor[][], - indexesAlias: string[] -): Promise => { - const fields = await formatFirstFields(responsesIndexFields, indexesAlias); - const secondFields = await formatSecondFields(fields); - return secondFields; -}; diff --git a/x-pack/plugins/security_solution/server/lib/index_fields/index.ts b/x-pack/plugins/security_solution/server/lib/index_fields/index.ts index a3ea8548bddc2c..94966bc16a407e 100644 --- a/x-pack/plugins/security_solution/server/lib/index_fields/index.ts +++ b/x-pack/plugins/security_solution/server/lib/index_fields/index.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IndexField } from '../../graphql/types'; - import { FieldsAdapter } from './types'; import { FrameworkRequest } from '../framework'; export { ElasticsearchIndexFieldAdapter } from './elasticsearch_adapter'; @@ -13,7 +11,8 @@ export { ElasticsearchIndexFieldAdapter } from './elasticsearch_adapter'; export class IndexFields { constructor(private readonly adapter: FieldsAdapter) {} - public async getFields(request: FrameworkRequest, defaultIndex: string[]): Promise { + // Deprecated until we delete all the code + public async getFields(request: FrameworkRequest, defaultIndex: string[]): Promise { return this.adapter.getIndexFields(request, defaultIndex); } } diff --git a/x-pack/plugins/security_solution/server/lib/index_fields/types.ts b/x-pack/plugins/security_solution/server/lib/index_fields/types.ts index 67b3c254007e24..fdc3509d0d452d 100644 --- a/x-pack/plugins/security_solution/server/lib/index_fields/types.ts +++ b/x-pack/plugins/security_solution/server/lib/index_fields/types.ts @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IndexField } from '../../graphql/types'; import { FrameworkRequest } from '../framework'; import { IFieldSubType } from '../../../../../../src/plugins/data/common'; export interface FieldsAdapter { - getIndexFields(req: FrameworkRequest, indices: string[]): Promise; + getIndexFields(req: FrameworkRequest, indices: string[]): Promise; } export interface IndexFieldDescriptor { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings.ts index c5ee611dfa27f0..11082cd7295cc4 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object_mappings.ts @@ -213,6 +213,9 @@ export const timelineSavedObjectMappings: SavedObjectsType['mappings'] = { }, }, }, + indexNames: { + type: 'text', + }, kqlMode: { type: 'keyword', }, diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 0571c4878956f3..22dbd623930c54 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -62,6 +62,7 @@ import { initUsageCollectors } from './usage'; import { AppRequestContext } from './types'; import { registerTrustedAppsRoutes } from './endpoint/routes/trusted_apps'; import { securitySolutionSearchStrategyProvider } from './search_strategy/security_solution'; +import { securitySolutionIndexFieldsProvider } from './search_strategy/index_fields'; import { securitySolutionTimelineSearchStrategyProvider } from './search_strategy/timeline'; export interface SetupPlugins { @@ -277,10 +278,16 @@ export class Plugin implements IPlugin { @@ -29,7 +24,7 @@ describe('Index Fields', () => { sortBy('name', [ { description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', + 'Date/time when the event originated. This is the date/time extracted from the event, typically representing when the event was generated by the source. If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. Required field for all events.', example: '2016-05-23T08:05:34.853Z', name: '@timestamp', type: 'date', @@ -37,41 +32,37 @@ describe('Index Fields', () => { aggregatable: true, category: 'base', indexes: ['auditbeat', 'filebeat', 'packetbeat'], + readFromDocValues: true, + esTypes: [], }, { description: 'Each document has an _id that uniquely identifies it', example: 'Y-6TfmcB0WOhS6qyMv3s', - footnote: '', - group: 1, - level: 'core', name: '_id', - required: true, type: 'string', searchable: true, aggregatable: false, - readFromDocValues: true, - category: '_id', + readFromDocValues: false, + category: 'base', indexes: ['auditbeat', 'filebeat', 'packetbeat'], + esTypes: [], }, { description: 'An index is like a ‘database’ in a relational database. It has a mapping which defines multiple types. An index is a logical namespace which maps to one or more primary shards and can have zero or more replica shards.', example: 'auditbeat-8.0.0-2019.02.19-000001', - footnote: '', - group: 1, - level: 'core', name: '_index', - required: true, type: 'string', searchable: true, aggregatable: true, - readFromDocValues: true, - category: '_index', + readFromDocValues: false, + category: 'base', indexes: ['auditbeat', 'filebeat', 'packetbeat'], + esTypes: [], }, { description: - 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', + 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', example: '8a4f500f', name: 'agent.ephemeral_id', type: 'string', @@ -79,18 +70,24 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat'], + readFromDocValues: false, + esTypes: [], }, { + description: + 'Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. ', name: 'agent.hostname', searchable: true, type: 'string', aggregatable: true, category: 'agent', indexes: ['filebeat'], + readFromDocValues: false, + esTypes: [], }, { description: - 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', + 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', example: '8a4f500d', name: 'agent.id', type: 'string', @@ -98,10 +95,12 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['packetbeat'], + readFromDocValues: false, + esTypes: [], }, { description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', + 'Custom name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', example: 'foo', name: 'agent.name', type: 'string', @@ -109,10 +108,12 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat', 'filebeat'], + readFromDocValues: false, + esTypes: [], }, { description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', + 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', example: 'filebeat', name: 'agent.type', type: 'string', @@ -120,6 +121,8 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat', 'packetbeat'], + readFromDocValues: false, + esTypes: [], }, { description: 'Version of the agent.', @@ -130,6 +133,8 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat', 'filebeat'], + readFromDocValues: false, + esTypes: [], }, ]) ); @@ -146,37 +151,31 @@ describe('Index Fields', () => { { description: 'Each document has an _id that uniquely identifies it', example: 'Y-6TfmcB0WOhS6qyMv3s', - footnote: '', - group: 1, - level: 'core', name: '_id', - required: true, type: 'string', searchable: true, aggregatable: false, - readFromDocValues: true, - category: '_id', + readFromDocValues: false, + category: 'base', indexes: ['auditbeat'], + esTypes: [], }, { description: 'An index is like a ‘database’ in a relational database. It has a mapping which defines multiple types. An index is a logical namespace which maps to one or more primary shards and can have zero or more replica shards.', example: 'auditbeat-8.0.0-2019.02.19-000001', - footnote: '', - group: 1, - level: 'core', name: '_index', - required: true, type: 'string', searchable: true, aggregatable: true, - readFromDocValues: true, - category: '_index', + readFromDocValues: false, + category: 'base', indexes: ['auditbeat'], + esTypes: [], }, { description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', + 'Date/time when the event originated. This is the date/time extracted from the event, typically representing when the event was generated by the source. If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. Required field for all events.', example: '2016-05-23T08:05:34.853Z', name: '@timestamp', type: 'date', @@ -184,10 +183,12 @@ describe('Index Fields', () => { aggregatable: true, category: 'base', indexes: ['auditbeat'], + readFromDocValues: true, + esTypes: [], }, { description: - 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', + 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', example: '8a4f500f', name: 'agent.ephemeral_id', type: 'string', @@ -195,10 +196,12 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat'], + readFromDocValues: false, + esTypes: [], }, { description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', + 'Custom name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', example: 'foo', name: 'agent.name', type: 'string', @@ -206,10 +209,12 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat'], + readFromDocValues: false, + esTypes: [], }, { description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', + 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', example: 'filebeat', name: 'agent.type', type: 'string', @@ -217,6 +222,8 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat'], + readFromDocValues: false, + esTypes: [], }, { description: 'Version of the agent.', @@ -227,41 +234,37 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat'], + readFromDocValues: false, + esTypes: [], }, { description: 'Each document has an _id that uniquely identifies it', example: 'Y-6TfmcB0WOhS6qyMv3s', - footnote: '', - group: 1, - level: 'core', name: '_id', - required: true, type: 'string', searchable: true, aggregatable: false, - readFromDocValues: true, - category: '_id', + category: 'base', indexes: ['filebeat'], + readFromDocValues: false, + esTypes: [], }, { description: 'An index is like a ‘database’ in a relational database. It has a mapping which defines multiple types. An index is a logical namespace which maps to one or more primary shards and can have zero or more replica shards.', example: 'auditbeat-8.0.0-2019.02.19-000001', - footnote: '', - group: 1, - level: 'core', name: '_index', - required: true, type: 'string', searchable: true, aggregatable: true, - readFromDocValues: true, - category: '_index', + category: 'base', indexes: ['filebeat'], + readFromDocValues: false, + esTypes: [], }, { description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', + 'Date/time when the event originated. This is the date/time extracted from the event, typically representing when the event was generated by the source. If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. Required field for all events.', example: '2016-05-23T08:05:34.853Z', name: '@timestamp', type: 'date', @@ -269,18 +272,24 @@ describe('Index Fields', () => { aggregatable: true, category: 'base', indexes: ['filebeat'], + readFromDocValues: true, + esTypes: [], }, { + description: + 'Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. ', name: 'agent.hostname', searchable: true, type: 'string', aggregatable: true, category: 'agent', indexes: ['filebeat'], + readFromDocValues: false, + esTypes: [], }, { description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', + 'Custom name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', example: 'foo', name: 'agent.name', type: 'string', @@ -288,6 +297,8 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['filebeat'], + readFromDocValues: false, + esTypes: [], }, { description: 'Version of the agent.', @@ -298,41 +309,37 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['filebeat'], + readFromDocValues: false, + esTypes: [], }, { description: 'Each document has an _id that uniquely identifies it', example: 'Y-6TfmcB0WOhS6qyMv3s', - footnote: '', - group: 1, - level: 'core', name: '_id', - required: true, type: 'string', searchable: true, aggregatable: false, - readFromDocValues: true, - category: '_id', + category: 'base', indexes: ['packetbeat'], + readFromDocValues: false, + esTypes: [], }, { description: 'An index is like a ‘database’ in a relational database. It has a mapping which defines multiple types. An index is a logical namespace which maps to one or more primary shards and can have zero or more replica shards.', example: 'auditbeat-8.0.0-2019.02.19-000001', - footnote: '', - group: 1, - level: 'core', name: '_index', - required: true, type: 'string', searchable: true, aggregatable: true, - readFromDocValues: true, - category: '_index', + category: 'base', indexes: ['packetbeat'], + readFromDocValues: false, + esTypes: [], }, { description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', + 'Date/time when the event originated. This is the date/time extracted from the event, typically representing when the event was generated by the source. If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. Required field for all events.', example: '2016-05-23T08:05:34.853Z', name: '@timestamp', type: 'date', @@ -340,10 +347,12 @@ describe('Index Fields', () => { aggregatable: true, category: 'base', indexes: ['packetbeat'], + readFromDocValues: true, + esTypes: [], }, { description: - 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', + 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', example: '8a4f500d', name: 'agent.id', type: 'string', @@ -351,10 +360,12 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['packetbeat'], + readFromDocValues: false, + esTypes: [], }, { description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', + 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', example: 'filebeat', name: 'agent.type', type: 'string', @@ -362,6 +373,8 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['packetbeat'], + readFromDocValues: false, + esTypes: [], }, ]); }); @@ -377,8 +390,9 @@ describe('Index Fields', () => { type: 'string', searchable: true, aggregatable: false, - category: '_id', + category: 'base', indexes: ['auditbeat'], + readFromDocValues: false, }, { description: @@ -388,12 +402,13 @@ describe('Index Fields', () => { type: 'string', searchable: true, aggregatable: true, - category: '_index', + category: 'base', indexes: ['auditbeat'], + readFromDocValues: false, }, { description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', + 'Date/time when the event originated. This is the date/time extracted from the event, typically representing when the event was generated by the source. If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. Required field for all events.', example: '2016-05-23T08:05:34.853Z', name: '@timestamp', type: 'date', @@ -401,10 +416,11 @@ describe('Index Fields', () => { aggregatable: true, category: 'base', indexes: ['auditbeat'], + readFromDocValues: true, }, { description: - 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', + 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', example: '8a4f500f', name: 'agent.ephemeral_id', type: 'string', @@ -412,10 +428,11 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat'], + readFromDocValues: false, }, { description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', + 'Custom name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', example: 'foo', name: 'agent.name', type: 'string', @@ -423,10 +440,11 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat'], + readFromDocValues: false, }, { description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', + 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', example: 'filebeat', name: 'agent.type', type: 'string', @@ -434,6 +452,7 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat'], + readFromDocValues: false, }, { description: 'Version of the agent.', @@ -444,6 +463,7 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat'], + readFromDocValues: false, }, { description: 'Each document has an _id that uniquely identifies it', @@ -452,8 +472,9 @@ describe('Index Fields', () => { type: 'string', searchable: true, aggregatable: false, - category: '_id', + category: 'base', indexes: ['filebeat'], + readFromDocValues: false, }, { description: @@ -463,12 +484,13 @@ describe('Index Fields', () => { type: 'string', searchable: true, aggregatable: true, - category: '_index', + category: 'base', indexes: ['filebeat'], + readFromDocValues: false, }, { description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', + 'Date/time when the event originated. This is the date/time extracted from the event, typically representing when the event was generated by the source. If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. Required field for all events.', example: '2016-05-23T08:05:34.853Z', name: '@timestamp', type: 'date', @@ -476,6 +498,7 @@ describe('Index Fields', () => { aggregatable: true, category: 'base', indexes: ['filebeat'], + readFromDocValues: true, }, { name: 'agent.hostname', @@ -484,10 +507,11 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['filebeat'], + readFromDocValues: false, }, { description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', + 'Custom name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', example: 'foo', name: 'agent.name', type: 'string', @@ -495,6 +519,7 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['filebeat'], + readFromDocValues: false, }, { description: 'Version of the agent.', @@ -505,6 +530,7 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['filebeat'], + readFromDocValues: false, }, { description: 'Each document has an _id that uniquely identifies it', @@ -513,8 +539,9 @@ describe('Index Fields', () => { type: 'string', searchable: true, aggregatable: false, - category: '_id', + category: 'base', indexes: ['packetbeat'], + readFromDocValues: false, }, { description: @@ -524,12 +551,13 @@ describe('Index Fields', () => { type: 'string', searchable: true, aggregatable: true, - category: '_index', + category: 'base', indexes: ['packetbeat'], + readFromDocValues: false, }, { description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', + 'Date/time when the event originated. This is the date/time extracted from the event, typically representing when the event was generated by the source. If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. Required field for all events.', example: '2016-05-23T08:05:34.853Z', name: '@timestamp', type: 'date', @@ -537,10 +565,11 @@ describe('Index Fields', () => { aggregatable: true, category: 'base', indexes: ['packetbeat'], + readFromDocValues: true, }, { description: - 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', + 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', example: '8a4f500d', name: 'agent.id', type: 'string', @@ -548,10 +577,11 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['packetbeat'], + readFromDocValues: false, }, { description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', + 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', example: 'filebeat', name: 'agent.type', type: 'string', @@ -559,6 +589,7 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['packetbeat'], + readFromDocValues: false, }, ]); expect(fields).toEqual([ @@ -569,8 +600,9 @@ describe('Index Fields', () => { type: 'string', searchable: true, aggregatable: false, - category: '_id', + category: 'base', indexes: ['auditbeat', 'filebeat', 'packetbeat'], + readFromDocValues: false, }, { description: @@ -580,12 +612,13 @@ describe('Index Fields', () => { type: 'string', searchable: true, aggregatable: true, - category: '_index', + category: 'base', indexes: ['auditbeat', 'filebeat', 'packetbeat'], + readFromDocValues: false, }, { description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', + 'Date/time when the event originated. This is the date/time extracted from the event, typically representing when the event was generated by the source. If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. Required field for all events.', example: '2016-05-23T08:05:34.853Z', name: '@timestamp', type: 'date', @@ -593,10 +626,11 @@ describe('Index Fields', () => { aggregatable: true, category: 'base', indexes: ['auditbeat', 'filebeat', 'packetbeat'], + readFromDocValues: true, }, { description: - 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', + 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', example: '8a4f500f', name: 'agent.ephemeral_id', type: 'string', @@ -604,10 +638,11 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat'], + readFromDocValues: false, }, { description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', + 'Custom name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', example: 'foo', name: 'agent.name', type: 'string', @@ -615,10 +650,11 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat', 'filebeat'], + readFromDocValues: false, }, { description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', + 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', example: 'filebeat', name: 'agent.type', type: 'string', @@ -626,6 +662,7 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat', 'packetbeat'], + readFromDocValues: false, }, { description: 'Version of the agent.', @@ -636,6 +673,7 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['auditbeat', 'filebeat'], + readFromDocValues: false, }, { name: 'agent.hostname', @@ -644,10 +682,11 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['filebeat'], + readFromDocValues: false, }, { description: - 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', + 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', example: '8a4f500d', name: 'agent.id', type: 'string', @@ -655,6 +694,7 @@ describe('Index Fields', () => { aggregatable: true, category: 'agent', indexes: ['packetbeat'], + readFromDocValues: false, }, ]); }); @@ -669,22 +709,22 @@ describe('Index Fields', () => { type: 'string', searchable: true, aggregatable: false, + readFromDocValues: false, + esTypes: [], }, 0 ); expect(item).toEqual({ description: 'Each document has an _id that uniquely identifies it', example: 'Y-6TfmcB0WOhS6qyMv3s', - footnote: '', - group: 1, - level: 'core', name: '_id', - required: true, type: 'string', searchable: true, aggregatable: false, - category: '_id', + category: 'base', indexes: ['auditbeat'], + readFromDocValues: false, + esTypes: [], }); }); }); diff --git a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts new file mode 100644 index 00000000000000..403a9425b221fa --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts @@ -0,0 +1,224 @@ +/* + * 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 isEmpty from 'lodash/isEmpty'; +import { IndexPatternsFetcher, ISearchStrategy } from '../../../../../../src/plugins/data/server'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { FieldDescriptor } from '../../../../../../src/plugins/data/server/index_patterns'; +import { + IndexFieldsStrategyResponse, + IndexField, + IndexFieldsStrategyRequest, +} from '../../../common/search_strategy/index_fields'; + +import { fieldsBeat } from '../../utils/beat_schema/fields'; + +export const securitySolutionIndexFieldsProvider = (): ISearchStrategy< + IndexFieldsStrategyRequest, + IndexFieldsStrategyResponse +> => { + return { + search: async (context, request) => { + const { elasticsearch } = context.core; + const indexPatternsFetcher = new IndexPatternsFetcher( + elasticsearch.legacy.client.callAsCurrentUser + ); + const dedupeIndices = dedupeIndexName(request.indices); + + const responsesIndexFields = await Promise.all( + dedupeIndices + .map((index) => + indexPatternsFetcher.getFieldsForWildcard({ + pattern: index, + }) + ) + .map((p) => p.catch((e) => false)) + ); + let indexFields: IndexField[] = []; + + if (!request.onlyCheckIfIndicesExist) { + indexFields = await formatIndexFields( + responsesIndexFields.filter((rif) => rif !== false) as FieldDescriptor[][], + dedupeIndices + ); + } + + return Promise.resolve({ + indexFields, + indicesExist: dedupeIndices.filter((index, i) => responsesIndexFields[i] !== false), + rawResponse: { + timed_out: false, + took: -1, + _shards: { + total: -1, + successful: -1, + failed: -1, + skipped: -1, + }, + hits: { + total: -1, + max_score: -1, + hits: [ + { + _index: '', + _type: '', + _id: '', + _score: -1, + _source: null, + }, + ], + }, + }, + }); + }, + }; +}; + +export const dedupeIndexName = (indices: string[]) => + indices.reduce((acc, index) => { + if (index.trim() !== '' && index.trim() !== '_all' && !acc.includes(index.trim())) { + return [...acc, index]; + } + return acc; + }, []); + +const missingFields: FieldDescriptor[] = [ + { + name: '_id', + type: 'string', + searchable: true, + aggregatable: false, + readFromDocValues: false, + esTypes: [], + }, + { + name: '_index', + type: 'string', + searchable: true, + aggregatable: true, + readFromDocValues: false, + esTypes: [], + }, +]; + +/** + * Creates a single field item. + * + * This is a mutatious HOT CODE PATH function that will have array sizes up to 4.7 megs + * in size at a time calling this function repeatedly. This function should be as optimized as possible + * and should avoid any and all creation of new arrays, iterating over the arrays or performing + * any n^2 operations. + * @param indexesAlias The index alias + * @param index The index its self + * @param indexesAliasIdx The index within the alias + */ +export const createFieldItem = ( + indexesAlias: string[], + index: FieldDescriptor, + indexesAliasIdx: number +): IndexField => { + const alias = indexesAlias[indexesAliasIdx]; + return { + ...(fieldsBeat[index.name] ?? {}), + ...index, + indexes: [alias], + }; +}; + +/** + * This is a mutatious HOT CODE PATH function that will have array sizes up to 4.7 megs + * in size at a time when being called. This function should be as optimized as possible + * and should avoid any and all creation of new arrays, iterating over the arrays or performing + * any n^2 operations. The `.push`, and `forEach` operations are expected within this function + * to speed up performance. + * + * This intentionally waits for the next tick on the event loop to process as the large 4.7 megs + * has already consumed a lot of the event loop processing up to this function and we want to give + * I/O opportunity to occur by scheduling this on the next loop. + * @param responsesIndexFields The response index fields to loop over + * @param indexesAlias The index aliases such as filebeat-* + */ +export const formatFirstFields = async ( + responsesIndexFields: FieldDescriptor[][], + indexesAlias: string[] +): Promise => { + return new Promise((resolve) => { + setTimeout(() => { + resolve( + responsesIndexFields.reduce( + (accumulator: IndexField[], indexFields: FieldDescriptor[], indexesAliasIdx: number) => { + missingFields.forEach((index) => { + const item = createFieldItem(indexesAlias, index, indexesAliasIdx); + accumulator.push(item); + }); + indexFields.forEach((index) => { + const item = createFieldItem(indexesAlias, index, indexesAliasIdx); + accumulator.push(item); + }); + return accumulator; + }, + [] + ) + ); + }); + }); +}; + +/** + * This is a mutatious HOT CODE PATH function that will have array sizes up to 4.7 megs + * in size at a time when being called. This function should be as optimized as possible + * and should avoid any and all creation of new arrays, iterating over the arrays or performing + * any n^2 operations. The `.push`, and `forEach` operations are expected within this function + * to speed up performance. The "indexFieldNameHash" side effect hash avoids additional expensive n^2 + * look ups. + * + * This intentionally waits for the next tick on the event loop to process as the large 4.7 megs + * has already consumed a lot of the event loop processing up to this function and we want to give + * I/O opportunity to occur by scheduling this on the next loop. + * @param fields The index fields to create the secondary fields for + */ +export const formatSecondFields = async (fields: IndexField[]): Promise => { + return new Promise((resolve) => { + setTimeout(() => { + const indexFieldNameHash: Record = {}; + const reduced = fields.reduce((accumulator: IndexField[], indexfield: IndexField) => { + const alreadyExistingIndexField = indexFieldNameHash[indexfield.name]; + if (alreadyExistingIndexField != null) { + const existingIndexField = accumulator[alreadyExistingIndexField]; + if (isEmpty(accumulator[alreadyExistingIndexField].description)) { + accumulator[alreadyExistingIndexField].description = indexfield.description; + } + accumulator[alreadyExistingIndexField].indexes = Array.from( + new Set([...existingIndexField.indexes, ...indexfield.indexes]) + ); + return accumulator; + } + accumulator.push(indexfield); + indexFieldNameHash[indexfield.name] = accumulator.length - 1; + return accumulator; + }, []); + resolve(reduced); + }); + }); +}; + +/** + * Formats the index fields into a format the UI wants. + * + * NOTE: This will have array sizes up to 4.7 megs in size at a time when being called. + * This function should be as optimized as possible and should avoid any and all creation + * of new arrays, iterating over the arrays or performing any n^2 operations. + * @param responsesIndexFields The response index fields to format + * @param indexesAlias The index alias + */ +export const formatIndexFields = async ( + responsesIndexFields: FieldDescriptor[][], + indexesAlias: string[] +): Promise => { + const fields = await formatFirstFields(responsesIndexFields, indexesAlias); + const secondFields = await formatSecondFields(fields); + return secondFields; +}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/index_fields/mock.ts b/x-pack/plugins/security_solution/server/search_strategy/index_fields/mock.ts new file mode 100644 index 00000000000000..efb992a868f655 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/index_fields/mock.ts @@ -0,0 +1,113 @@ +/* + * 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. + */ + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { FieldDescriptor } from '../../../../../../src/plugins/data/server/index_patterns'; + +export const mockAuditbeatIndexField: FieldDescriptor[] = [ + { + name: '@timestamp', + searchable: true, + type: 'date', + aggregatable: true, + readFromDocValues: true, + esTypes: [], + }, + { + name: 'agent.ephemeral_id', + searchable: true, + type: 'string', + aggregatable: true, + readFromDocValues: false, + esTypes: [], + }, + { + name: 'agent.name', + searchable: true, + type: 'string', + aggregatable: true, + readFromDocValues: false, + esTypes: [], + }, + { + name: 'agent.type', + searchable: true, + type: 'string', + aggregatable: true, + readFromDocValues: false, + esTypes: [], + }, + { + name: 'agent.version', + searchable: true, + type: 'string', + aggregatable: true, + readFromDocValues: false, + esTypes: [], + }, +]; + +export const mockFilebeatIndexField: FieldDescriptor[] = [ + { + name: '@timestamp', + searchable: true, + type: 'date', + aggregatable: true, + readFromDocValues: true, + esTypes: [], + }, + { + name: 'agent.hostname', + searchable: true, + type: 'string', + aggregatable: true, + readFromDocValues: false, + esTypes: [], + }, + { + name: 'agent.name', + searchable: true, + type: 'string', + aggregatable: true, + readFromDocValues: false, + esTypes: [], + }, + { + name: 'agent.version', + searchable: true, + type: 'string', + aggregatable: true, + readFromDocValues: false, + esTypes: [], + }, +]; + +export const mockPacketbeatIndexField: FieldDescriptor[] = [ + { + name: '@timestamp', + searchable: true, + type: 'date', + aggregatable: true, + readFromDocValues: true, + esTypes: [], + }, + { + name: 'agent.id', + searchable: true, + type: 'string', + aggregatable: true, + readFromDocValues: false, + esTypes: [], + }, + { + name: 'agent.type', + searchable: true, + type: 'string', + aggregatable: true, + readFromDocValues: false, + esTypes: [], + }, +]; diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts index edff22766cc54e..b2ce57d87ae6d8 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts @@ -19,6 +19,7 @@ export const formatTimelineData = ( flattenedFields.node._id = hit._id; flattenedFields.node._index = hit._index; flattenedFields.node.ecs._id = hit._id; + flattenedFields.node.ecs.timestamp = hit._source['@timestamp']; flattenedFields.node.ecs._index = hit._index; if (hit.sort && hit.sort.length > 1) { flattenedFields.cursor.value = hit.sort[0]; diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/details/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/details/helpers.ts index 3b0935db9a5d66..2dd406ffaa4504 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/details/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/details/helpers.ts @@ -7,7 +7,8 @@ import { get, isEmpty, isNumber, isObject, isString } from 'lodash/fp'; import { TimelineEventsDetailsItem } from '../../../../../../common/search_strategy/timeline'; -import { baseCategoryFields } from '../../../../../utils/beat_schema/8.0.0'; + +export const baseCategoryFields = ['@timestamp', 'labels', 'message', 'tags']; export const getFieldCategory = (field: string): string => { const fieldCategory = field.split('.')[0]; diff --git a/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/auditbeat.ts b/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/auditbeat.ts deleted file mode 100644 index 76c865679dd058..00000000000000 --- a/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/auditbeat.ts +++ /dev/null @@ -1,7902 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/** - * An instance of the unmodified schema exported from auditbeat-8.0.0-SNAPSHOT-darwin-x86_64.tar.gz - * - */ - -import { Schema } from '../type'; - -export const auditbeatSchema: Schema = [ - { - key: 'ecs', - title: 'ECS', - description: 'ECS Fields.', - fields: [ - { - name: '@timestamp', - level: 'core', - required: true, - type: 'date', - description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', - example: '2016-05-23T08:05:34.853Z', - }, - { - name: 'labels', - level: 'core', - type: 'object', - object_type: 'keyword', - description: - 'Custom key/value pairs.\n\nCan be used to add meta information to events. Should not contain nested objects.\nAll values are stored as keyword.\n\nExample: `docker` and `k8s` labels.', - example: '{"application": "foo-bar", "env": "production"}', - }, - { - name: 'message', - level: 'core', - type: 'text', - description: - 'For log events the message field contains the log message, optimized\nfor viewing in a log viewer.\n\nFor structured logs without an original message field, other fields can be concatenated\nto form a human-readable summary of the event.\n\nIf multiple messages exist, they can be combined into one message.', - example: 'Hello World', - }, - { - name: 'tags', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - }, - { - name: 'agent', - title: 'Agent', - group: 2, - description: - 'The agent fields contain the data about the software entity, if\nany, that collects, detects, or observes events on a host, or takes measurements\non a host.\n\nExamples include Beats. Agents may also run on observers. ECS agent.* fields\nshall be populated with details of the agent running on the host or observer\nwhere the event happened or the measurement was taken.', - footnote: - 'Examples: In the case of Beats for logs, the agent.name is filebeat.\nFor APM, it is the agent running in the app/service. The agent information does\nnot change if data is sent through queuing systems like Kafka, Redis, or processing\nsystems such as Logstash or APM Server.', - type: 'group', - fields: [ - { - name: 'ephemeral_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', - example: '8a4f500d', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', - example: 'foo', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', - example: 'filebeat', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Version of the agent.', - example: '6.0.0-rc2', - }, - ], - }, - { - name: 'as', - title: 'Autonomous System', - group: 2, - description: - 'An autonomous system (AS) is a collection of connected Internet Protocol\n(IP) routing prefixes under the control of one or more network operators on\nbehalf of a single administrative entity or domain that presents a common, clearly\ndefined routing policy to the internet.', - type: 'group', - fields: [ - { - name: 'number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - ], - }, - { - name: 'client', - title: 'Client', - group: 2, - description: - 'A client is defined as the initiator of a network connection for\nevents regarding sessions, connections, or bidirectional flow records.\n\nFor TCP events, the client is the initiator of the TCP connection that sends\nthe SYN packet(s). For other protocols, the client is generally the initiator\nor requestor in the network transaction. Some systems use the term "originator"\nto refer the client in TCP connections. The client fields describe details about\nthe system acting as the client in the network event. Client fields are usually\npopulated in conjunction with server fields. Client fields are generally not\npopulated for packet-level events.\n\nClient / server representations can add semantic context to an exchange, which\nis helpful to visualize the data in certain situations. If your context falls\nin that category, you should still ensure that source and destination are filled\nappropriately.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event client addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the client to the server.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Client domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the client.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the client.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated IP of source based NAT sessions (e.g. internal client\nto internet).\n\nTypically connections traversing load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Translated port of source based NAT sessions (e.g. internal client\nto internet).\n\nTypically connections traversing load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the client to the server.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the client.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered client domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'cloud', - title: 'Cloud', - group: 2, - description: 'Fields related to the cloud or infrastructure the events are coming\nfrom.', - footnote: - 'Examples: If Metricbeat is running on an EC2 host and fetches data\nfrom its host, the cloud info contains the data about this machine. If Metricbeat\nruns on a remote machine outside the cloud and fetches data from a service running\nin the cloud, the field contains cloud data from the machine the service is\nrunning on.', - type: 'group', - fields: [ - { - name: 'account.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The cloud account or organization id used to identify different\nentities in a multi-tenant environment.\n\nExamples: AWS account id, Google Cloud ORG Id, or other unique identifier.', - example: 666777888999, - }, - { - name: 'availability_zone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Availability zone in which this host is running.', - example: 'us-east-1c', - }, - { - name: 'instance.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Instance ID of the host machine.', - example: 'i-1234567890abcdef0', - }, - { - name: 'instance.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Instance name of the host machine.', - }, - { - name: 'machine.type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Machine type of the host machine.', - example: 't2.medium', - }, - { - name: 'provider', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the cloud provider. Example values are aws, azure, gcp,\nor digitalocean.', - example: 'aws', - }, - { - name: 'region', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Region in which this host is running.', - example: 'us-east-1', - }, - ], - }, - { - name: 'code_signature', - title: 'Code Signature', - group: 2, - description: 'These fields contain information about binary code signatures.', - type: 'group', - fields: [ - { - name: 'exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - ], - }, - { - name: 'container', - title: 'Container', - group: 2, - description: - 'Container fields are used for meta information about the specific\ncontainer that is the source of information.\n\nThese fields help correlate data based containers from any runtime.', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique container id.', - }, - { - name: 'image.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the image the container was built on.', - }, - { - name: 'image.tag', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Container image tags.', - }, - { - name: 'labels', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: 'Image labels.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Container name.', - }, - { - name: 'runtime', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Runtime managing this container.', - example: 'docker', - }, - ], - }, - { - name: 'destination', - title: 'Destination', - group: 2, - description: - 'Destination fields describe details about the destination of a packet/event.\n\nDestination fields are usually populated in conjunction with source fields.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event destination addresses are defined ambiguously. The\nevent will sometimes list an IP, a domain or a unix socket. You should always\nstore the raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the destination to the source.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Destination domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the destination.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the destination.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated ip of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Port the source session is translated to by NAT Device.\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the destination to the source.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the destination.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered destination domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'dll', - title: 'DLL', - group: 2, - description: - 'These fields contain information about code libraries dynamically\nloaded into processes.\n\n\nMany operating systems refer to "shared code libraries" with different names,\nbut this field set refers to all of the following:\n\n* Dynamic-link library (`.dll`) commonly used on Windows\n\n* Shared Object (`.so`) commonly used on Unix-like operating systems\n\n* Dynamic library (`.dylib`) commonly used on macOS', - type: 'group', - fields: [ - { - name: 'code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - default_field: false, - }, - { - name: 'hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - default_field: false, - }, - { - name: 'hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - default_field: false, - }, - { - name: 'hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - default_field: false, - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the library.\n\nThis generally maps to the name of the file on disk.', - example: 'kernel32.dll', - default_field: false, - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Full file path of the library.', - example: 'C:\\Windows\\System32\\kernel32.dll', - default_field: false, - }, - { - name: 'pe.company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'pe.description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'pe.file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'pe.original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'pe.product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - ], - }, - { - name: 'dns', - title: 'DNS', - group: 2, - description: - 'Fields describing DNS queries and answers.\n\nDNS events should either represent a single DNS query prior to getting answers\n(`dns.type:query`) or they should represent a full exchange and contain the\nquery details as well as all of the answers that were provided for this query\n(`dns.type:answer`).', - type: 'group', - fields: [ - { - name: 'answers', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'An array containing an object for each answer section returned\nby the server.\n\nThe main keys that should be present in these objects are defined by ECS.\nRecords that have more information may contain more keys than what ECS defines.\n\nNot all DNS data sources give all details about DNS answers. At minimum, answer\nobjects must contain the `data` key. If more information is available, map\nas much of it to ECS as possible, and add any additional fields to the answer\nobjects as custom fields.', - }, - { - name: 'answers.class', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The class of DNS data contained in this resource record.', - example: 'IN', - }, - { - name: 'answers.data', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The data describing the resource.\n\nThe meaning of this data depends on the type and class of the resource record.', - example: '10.10.10.10', - }, - { - name: 'answers.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The domain name to which this resource record pertains.\n\nIf a chain of CNAME is being resolved, each answer `name` should be the\none that corresponds with the answer `data`. It should not simply be the\noriginal `question.name` repeated.', - example: 'www.google.com', - }, - { - name: 'answers.ttl', - level: 'extended', - type: 'long', - description: - 'The time interval in seconds that this resource record may be cached\nbefore it should be discarded. Zero values mean that the data should not be\ncached.', - example: 180, - }, - { - name: 'answers.type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The type of data contained in this resource record.', - example: 'CNAME', - }, - { - name: 'header_flags', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of 2 letter DNS header flags.\n\nExpected values are: AA, TC, RD, RA, AD, CD, DO.', - example: ['RD', 'RA'], - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The DNS packet identifier assigned by the program that generated\nthe query. The identifier is copied to the response.', - example: 62111, - }, - { - name: 'op_code', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The DNS operation code that specifies the kind of query in the\nmessage. This value is set by the originator of a query and copied into the\nresponse.', - example: 'QUERY', - }, - { - name: 'question.class', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The class of records being queried.', - example: 'IN', - }, - { - name: 'question.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The name being queried.\n\nIf the name field contains non-printable characters (below 32 or above 126),\nthose characters should be represented as escaped base 10 integers (\\DDD).\nBack slashes and quotes should be escaped. Tabs, carriage returns, and line\nfeeds should be converted to \\t, \\r, and \\n respectively.', - example: 'www.google.com', - }, - { - name: 'question.registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'question.subdomain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The subdomain is all of the labels under the registered_domain.\n\nIf the domain has multiple levels of subdomain, such as "sub2.sub1.example.com",\nthe subdomain field should contain "sub2.sub1", with no trailing period.', - example: 'www', - }, - { - name: 'question.top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'question.type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The type of record being queried.', - example: 'AAAA', - }, - { - name: 'resolved_ip', - level: 'extended', - type: 'ip', - description: - 'Array containing all IPs seen in `answers.data`.\n\nThe `answers` array can be difficult to use, because of the variety of data\nformats it can contain. Extracting all IP addresses seen in there to `dns.resolved_ip`\nmakes it possible to index them as IP addresses, and makes them easier to\nvisualize and query for.', - example: ['10.10.10.10', '10.10.10.11'], - }, - { - name: 'response_code', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The DNS response code.', - example: 'NOERROR', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of DNS event captured, query or answer.\n\nIf your source of DNS events only gives you DNS queries, you should only create\ndns events of type `dns.type:query`.\n\nIf your source of DNS events gives you answers as well, you should create\none event per query (optionally as soon as the query is seen). And a second\nevent containing all query details as well as an array of answers.', - example: 'answer', - }, - ], - }, - { - name: 'ecs', - title: 'ECS', - group: 2, - description: 'Meta-information specific to ECS.', - type: 'group', - fields: [ - { - name: 'version', - level: 'core', - required: true, - type: 'keyword', - ignore_above: 1024, - description: - 'ECS version this event conforms to. `ecs.version` is a required\nfield and must exist in all events.\n\nWhen querying across multiple indices -- which may conform to slightly different\nECS versions -- this field lets integrations adjust to the schema version\nof the events.', - example: '1.0.0', - }, - ], - }, - { - name: 'error', - title: 'Error', - group: 2, - description: - 'These fields can represent errors of any kind.\n\nUse them for errors that happen while fetching events or in cases where the\nevent itself contains an error.', - type: 'group', - fields: [ - { - name: 'code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Error code describing the error.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the error.', - }, - { - name: 'message', - level: 'core', - type: 'text', - description: 'Error message.', - }, - { - name: 'stack_trace', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The stack trace of this error in plain text.', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The type of the error, for example the class name of the exception.', - example: 'java.lang.NullPointerException', - }, - ], - }, - { - name: 'event', - title: 'Event', - group: 2, - description: - 'The event fields are used for context information about the log\nor metric event itself.\n\nA log is defined as an event containing details of something that happened.\nLog events must include the time at which the thing happened. Examples of log\nevents include a process starting on a host, a network packet being sent from\na source to a destination, or a network connection between a client and a server\nbeing initiated or closed. A metric is defined as an event containing one or\nmore numerical measurements and the time at which the measurement was taken.\nExamples of metric events include memory pressure measured on a host and device\ntemperature. See the `event.kind` definition in this section for additional\ndetails about metric and state events.', - type: 'group', - fields: [ - { - name: 'action', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The action captured by the event.\n\nThis describes the information in the event. It is more specific than `event.category`.\nExamples are `group-add`, `process-started`, `file-created`. The value is\nnormally defined by the implementer.', - example: 'user-password-change', - }, - { - name: 'category', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nsecond level in the ECS category hierarchy.\n\n`event.category` represents the "big buckets" of ECS categories. For example,\nfiltering on `event.category:process` yields all events relating to process\nactivity. This field is closely related to `event.type`, which is used as\na subcategory.\n\nThis field is an array. This will allow proper categorization of some events\nthat fall in multiple categories.', - example: 'authentication', - }, - { - name: 'code', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Identification code for this event, if one exists.\n\nSome event sources use event codes to identify messages unambiguously, regardless\nof message language or wording adjustments over time. An example of this is\nthe Windows Event ID.', - example: 4648, - }, - { - name: 'created', - level: 'core', - type: 'date', - description: - 'event.created contains the date/time when the event was first\nread by an agent, or by your pipeline.\n\nThis field is distinct from @timestamp in that @timestamp typically contain\nthe time extracted from the original event.\n\nIn most situations, these two timestamps will be slightly different. The difference\ncan be used to calculate the delay between your source generating an event,\nand the time when your agent first processed it. This can be used to monitor\nyour agent or pipeline ability to keep up with your event source.\n\nIn case the two timestamps are identical, @timestamp should be used.', - example: '2016-05-23T08:05:34.857Z', - }, - { - name: 'dataset', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the dataset.\n\nIf an event source publishes more than one type of log or events (e.g. access\nlog, error log), the dataset is used to specify which one the event comes\nfrom.\n\nIt is recommended but not required to start the dataset name with the module\nname, followed by a dot, then the dataset name.', - example: 'apache.access', - }, - { - name: 'duration', - level: 'core', - type: 'long', - format: 'duration', - input_format: 'nanoseconds', - output_format: 'asMilliseconds', - output_precision: 1, - description: - 'Duration of the event in nanoseconds.\n\nIf event.start and event.end are known this value should be the difference\nbetween the end and start time.', - }, - { - name: 'end', - level: 'extended', - type: 'date', - description: - 'event.end contains the date when the event ended or when the activity\nwas last observed.', - }, - { - name: 'hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Hash (perhaps logstash fingerprint) of raw field to be able to\ndemonstrate log integrity.', - example: '123456789012345678901234567890ABCD', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique ID to describe the event.', - example: '8a4f500d', - }, - { - name: 'ingested', - level: 'core', - type: 'date', - description: - 'Timestamp when an event arrived in the central data store.\n\nThis is different from `@timestamp`, which is when the event originally occurred. It is\nalso different from `event.created`, which is meant to capture the first time\nan agent saw the event.\n\nIn normal conditions, assuming no tampering, the timestamps should chronologically\nlook like this: `@timestamp` < `event.created` < `event.ingested`.', - example: '2016-05-23T08:05:35.101Z', - default_field: false, - }, - { - name: 'kind', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nhighest level in the ECS category hierarchy.\n\n`event.kind` gives high-level information about what type of information the\nevent contains, without being specific to the contents of the event. For example,\nvalues of this field distinguish alert events from metric events.\n\nThe value of this field can be used to inform how these kinds of events should\nbe handled. They may warrant different retention, different access control,\nit may also help understand whether the data coming in at a regular interval\nor not.', - example: 'alert', - }, - { - name: 'module', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the module this data is coming from.\n\nIf your monitoring agent supports the concept of modules or plugins to process\nevents of a given source (e.g. Apache logs), `event.module` should contain\nthe name of this module.', - example: 'apache', - }, - { - name: 'original', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Raw text message of entire event. Used to demonstrate log integrity.\n\nThis field is not indexed and doc_values are disabled. It cannot be searched,\nbut it can be retrieved from `_source`.', - example: - 'Sep 19 08:26:10 host CEF:0|Security| threatmanager|1.0|100|\nworm successfully stopped|10|src=10.0.0.1 dst=2.1.2.2spt=1232', - }, - { - name: 'outcome', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nlowest level in the ECS category hierarchy.\n\n`event.outcome` simply denotes whether the event represents a success or a\nfailure from the perspective of the entity that produced the event.\n\nNote that when a single transaction is described in multiple events, each\nevent may populate different values of `event.outcome`, according to their\nperspective.\n\nAlso note that in the case of a compound event (a single event that contains\nmultiple logical events), this field should be populated with the value that\nbest captures the overall success or failure from the perspective of the event\nproducer.\n\nFurther note that not all events will have an associated outcome. For example,\nthis field is generally not populated for metric events, events with `event.type:info`,\nor any events for which an outcome does not make logical sense.', - example: 'success', - }, - { - name: 'provider', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Source of the event.\n\nEvent transports such as Syslog or the Windows Event Log typically mention\nthe source of an event. It can be the name of the software that generated\nthe event (e.g. Sysmon, httpd), or of a subsystem of the operating system\n(kernel, Microsoft-Windows-Security-Auditing).', - example: 'kernel', - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Reference URL linking to additional information about this event.\n\nThis URL links to a static definition of the this event. Alert events, indicated\nby `event.kind:alert`, are a common use case for this field.', - example: 'https://system.vendor.com/event/#0001234', - default_field: false, - }, - { - name: 'risk_score', - level: 'core', - type: 'float', - description: - "Risk score or priority of the event (e.g. security solutions).\nUse your system's original value here.", - }, - { - name: 'risk_score_norm', - level: 'extended', - type: 'float', - description: - 'Normalized risk score or priority of the event, on a scale of\n0 to 100.\n\nThis is mainly useful if you use more than one system that assigns risk scores,\nand you want to see a normalized value across all systems.', - }, - { - name: 'sequence', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Sequence number of the event.\n\nThe sequence number is a value published by some event sources, to make the\nexact ordering of events unambiguous, regardless of the timestamp precision.', - }, - { - name: 'severity', - level: 'core', - type: 'long', - format: 'string', - description: - 'The numeric severity of the event according to your event source.\n\nWhat the different severity values mean can be different between sources and\nuse cases. It is up to the implementer to make sure severities are consistent\nacross events from the same source.\n\nThe Syslog severity belongs in `log.syslog.severity.code`. `event.severity`\nis meant to represent the severity according to the event source (e.g. firewall,\nIDS). If the event source does not publish its own severity, you may optionally\ncopy the `log.syslog.severity.code` to `event.severity`.', - example: 7, - }, - { - name: 'start', - level: 'extended', - type: 'date', - description: - 'event.start contains the date when the event started or when the\nactivity was first observed.', - }, - { - name: 'timezone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'This field should be populated when the event timestamp does\nnot include timezone information already (e.g. default Syslog timestamps).\nIt is optional otherwise.\n\nAcceptable timezone formats are: a canonical ID (e.g. "Europe/Amsterdam"),\nabbreviated (e.g. "EST") or an HH:mm differential (e.g. "-05:00").', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nthird level in the ECS category hierarchy.\n\n`event.type` represents a categorization "sub-bucket" that, when used along\nwith the `event.category` field values, enables filtering events down to a\nlevel appropriate for single visualization.\n\nThis field is an array. This will allow proper categorization of some events\nthat fall in multiple event types.', - }, - { - name: 'url', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'URL linking to an external system to continue investigation of\nthis event.\n\nThis URL links to another system where in-depth investigation of the specific\noccurence of this event can take place. Alert events, indicated by `event.kind:alert`,\nare a common use case for this field.', - example: 'https://mysystem.mydomain.com/alert/5271dedb-f5b0-4218-87f0-4ac4870a38fe', - default_field: false, - }, - ], - }, - { - name: 'file', - title: 'File', - group: 2, - description: - 'A file is defined as a set of information that has been created\non, or has existed on a filesystem.\n\nFile objects can be associated with host events, network events, and/or file\nevents (e.g., those produced by File Integrity Monitoring [FIM] products or\nservices). File fields provide details about the affected file associated with\nthe event or metric.', - type: 'group', - fields: [ - { - name: 'accessed', - level: 'extended', - type: 'date', - description: - 'Last time the file was accessed.\n\nNote that not all filesystems keep track of access time.', - }, - { - name: 'attributes', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of file attributes.\n\nAttributes names will vary by platform. Here is a non-exhaustive list of values\nthat are expected in this field: archive, compressed, directory, encrypted,\nexecute, hidden, read, readonly, system, write.', - example: '["readonly", "system"]', - default_field: false, - }, - { - name: 'code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'created', - level: 'extended', - type: 'date', - description: - 'File creation time.\n\nNote that not all filesystems store the creation time.', - }, - { - name: 'ctime', - level: 'extended', - type: 'date', - description: - 'Last time the file attributes or metadata changed.\n\nNote that changes to the file content will update `mtime`. This implies `ctime`\nwill be adjusted at the same time, since `mtime` is an attribute of the file.', - }, - { - name: 'device', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Device that is the source of the file.', - example: 'sda', - }, - { - name: 'directory', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Directory where the file is located. It should include the drive\nletter, when appropriate.', - example: '/home/alice', - }, - { - name: 'drive_letter', - level: 'extended', - type: 'keyword', - ignore_above: 1, - description: - 'Drive letter where the file is located. This field is only relevant\non Windows.\n\nThe value should be uppercase, and not include the colon.', - example: 'C', - default_field: false, - }, - { - name: 'extension', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'File extension.', - example: 'png', - }, - { - name: 'gid', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Primary group ID (GID) of the file.', - example: '1001', - }, - { - name: 'group', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Primary group name of the file.', - example: 'alice', - }, - { - name: 'hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - }, - { - name: 'hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - }, - { - name: 'hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - }, - { - name: 'hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - }, - { - name: 'inode', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Inode representing the file in the filesystem.', - example: '256383', - }, - { - name: 'mime_type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'MIME type should identify the format of the file or stream of bytes\nusing https://www.iana.org/assignments/media-types/media-types.xhtml[IANA\nofficial types], where possible. When more than one type is applicable, the\nmost specific type should be used.', - default_field: false, - }, - { - name: 'mode', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Mode of the file in octal representation.', - example: '0640', - }, - { - name: 'mtime', - level: 'extended', - type: 'date', - description: 'Last time the file content was modified.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the file including the extension, without the directory.', - example: 'example.png', - }, - { - name: 'owner', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: "File owner's username.", - example: 'alice', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'Full path to the file, including the file name. It should include\nthe drive letter, when appropriate.', - example: '/home/alice/example.png', - }, - { - name: 'pe.company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'pe.description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'pe.file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'pe.original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'pe.product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - { - name: 'size', - level: 'extended', - type: 'long', - description: 'File size in bytes.\n\nOnly relevant when `file.type` is "file".', - example: 16384, - }, - { - name: 'target_path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Target path for symlinks.', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'File type (file, dir, or symlink).', - example: 'file', - }, - { - name: 'uid', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The user ID (UID) or security identifier (SID) of the file owner.', - example: '1001', - }, - ], - }, - { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an\nevent.\n\nThis geolocation information can be derived from techniques such as Geo IP,\nor be user-supplied.', - type: 'group', - fields: [ - { - name: 'city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - ], - }, - { - name: 'group', - title: 'Group', - group: 2, - description: - 'The group fields are meant to represent groups that are relevant\nto the event.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - ], - }, - { - name: 'hash', - title: 'Hash', - group: 2, - description: - 'The hash fields represent different hash algorithms and their values.\n\nField names for common hashes (e.g. MD5, SHA1) are predefined. Add fields for\nother hashes by lowercasing the hash algorithm name and using underscore separators\nas appropriate (snake case, e.g. sha3_512).', - type: 'group', - fields: [ - { - name: 'md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - }, - { - name: 'sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - }, - { - name: 'sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - }, - { - name: 'sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - }, - ], - }, - { - name: 'host', - title: 'Host', - group: 2, - description: - 'A host is defined as a general computing instance.\n\nECS host.* fields should be populated with details about the host on which the\nevent happened, or from which the measurement was taken. Host types include\nhardware, virtual machines, Docker containers, and Kubernetes nodes.', - type: 'group', - fields: [ - { - name: 'architecture', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system architecture.', - example: 'x86_64', - }, - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the domain of which the host is a member.\n\nFor example, on Windows this could be the host Active Directory domain\nor NetBIOS domain name. For Linux this could be the domain of the host\nLDAP provider.', - example: 'CONTOSO', - default_field: false, - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'hostname', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Hostname of the host.\n\nIt normally contains what the `hostname` command returns on the host machine.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique host id.\n\nAs hostname is not always unique, use values that are meaningful in your environment.\n\nExample: The current usage of `beat.name`.', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'Host ip addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Host mac addresses.', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Type of host.\n\nFor Cloud providers this can be the machine type like `t2.medium`. If vm,\nthis could be the container, for example, or other information meaningful\nin your environment.', - }, - { - name: 'uptime', - level: 'extended', - type: 'long', - description: 'Seconds the host has been up.', - example: 1325, - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'http', - title: 'HTTP', - group: 2, - description: - 'Fields related to HTTP activity. Use the `url` field set to store\nthe url of the request.', - type: 'group', - fields: [ - { - name: 'request.body.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the request body.', - example: 887, - }, - { - name: 'request.body.content', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The full HTTP request body.', - example: 'Hello world', - }, - { - name: 'request.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the request (body and headers).', - example: 1437, - }, - { - name: 'request.method', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'HTTP request method.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'get, post, put', - }, - { - name: 'request.referrer', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Referrer for this HTTP request.', - example: 'https://blog.example.com/', - }, - { - name: 'response.body.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the response body.', - example: 887, - }, - { - name: 'response.body.content', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The full HTTP response body.', - example: 'Hello world', - }, - { - name: 'response.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the response (body and headers).', - example: 1437, - }, - { - name: 'response.status_code', - level: 'extended', - type: 'long', - format: 'string', - description: 'HTTP response status code.', - example: 404, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'HTTP version.', - example: 1.1, - }, - ], - }, - { - name: 'interface', - title: 'Interface', - group: 2, - description: - 'The interface fields are used to record ingress and egress interface\ninformation when reported by an observer (e.g. firewall, router, load balancer)\nin the context of the observer handling a network connection. In the case of\na single observer interface (e.g. network sensor on a span port) only the observer.ingress\ninformation should be populated.', - type: 'group', - fields: [ - { - name: 'alias', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Interface alias as reported by the system, typically used in firewall\nimplementations for e.g. inside, outside, or dmz logical interface naming.', - example: 'outside', - default_field: false, - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface ID as reported by an observer (typically SNMP interface\nID).', - example: 10, - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface name as reported by the system.', - example: 'eth0', - default_field: false, - }, - ], - }, - { - name: 'log', - title: 'Log', - group: 2, - description: - 'Details about the event logging mechanism or logging transport.\n\nThe log.* fields are typically populated with details about the logging mechanism\nused to create and/or transport the event. For example, syslog details belong\nunder `log.syslog.*`.\n\nThe details specific to your event source are typically not logged under `log.*`,\nbut rather in `event.*` or in other ECS fields.', - type: 'group', - fields: [ - { - name: 'level', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Original log level of the log event.\n\nIf the source of the event provides a log level or textual severity, this\nis the one that goes in `log.level`. If your source does not specify one,\nyou may put your event transport severity here (e.g. Syslog severity).\n\nSome examples are `warn`, `err`, `i`, `informational`.', - example: 'error', - }, - { - name: 'logger', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The name of the logger inside an application. This is usually the\nname of the class which initialized the logger, or can be a custom name.', - example: 'org.elasticsearch.bootstrap.Bootstrap', - }, - { - name: 'origin.file.line', - level: 'extended', - type: 'integer', - description: - 'The line number of the file containing the source code which originated\nthe log event.', - example: 42, - }, - { - name: 'origin.file.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The name of the file containing the source code which originated\nthe log event. Note that this is not the name of the log file.', - example: 'Bootstrap.java', - }, - { - name: 'origin.function', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The name of the function or method which originated the log event.', - example: 'init', - }, - { - name: 'original', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is the original log message and contains the full log message\nbefore splitting it up in multiple parts.\n\nIn contrast to the `message` field which can contain an extracted part of\nthe log message, this field contains the original, full log message. It can\nhave already some modifications applied like encoding or new lines removed\nto clean up the log message.\n\nThis field is not indexed and doc_values are disabled so it cannot be queried\nbut the value can be retrieved from `_source`.', - example: 'Sep 19 08:26:10 localhost My log', - }, - { - name: 'syslog', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'The Syslog metadata of the event, if the event was transmitted\nvia Syslog. Please see RFCs 5424 or 3164.', - }, - { - name: 'syslog.facility.code', - level: 'extended', - type: 'long', - format: 'string', - description: - 'The Syslog numeric facility of the log event, if available.\n\nAccording to RFCs 5424 and 3164, this value should be an integer between 0\nand 23.', - example: 23, - }, - { - name: 'syslog.facility.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The Syslog text-based facility of the log event, if available.', - example: 'local7', - }, - { - name: 'syslog.priority', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Syslog numeric priority of the event, if available.\n\nAccording to RFCs 5424 and 3164, the priority is 8 * facility + severity.\nThis number is therefore expected to contain a value between 0 and 191.', - example: 135, - }, - { - name: 'syslog.severity.code', - level: 'extended', - type: 'long', - description: - 'The Syslog numeric severity of the log event, if available.\n\nIf the event source publishing via Syslog provides a different numeric severity\nvalue (e.g. firewall, IDS), your source numeric severity should go to `event.severity`.\nIf the event source does not specify a distinct severity, you can optionally\ncopy the Syslog severity to `event.severity`.', - example: 3, - }, - { - name: 'syslog.severity.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The Syslog numeric severity of the log event, if available.\n\nIf the event source publishing via Syslog provides a different severity value\n(e.g. firewall, IDS), your source text severity should go to `log.level`.\nIf the event source does not specify a distinct severity, you can optionally\ncopy the Syslog severity to `log.level`.', - example: 'Error', - }, - ], - }, - { - name: 'network', - title: 'Network', - group: 2, - description: - 'The network is defined as the communication path over which a host\nor network event happens.\n\nThe network.* fields should be populated with details about the network activity\nassociated with an event.', - type: 'group', - fields: [ - { - name: 'application', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A name given to an application level protocol. This can be arbitrarily\nassigned for things like microservices, but also apply to things like skype,\nicq, facebook, twitter. This would be used in situations where the vendor\nor service can be decoded such as from the source/dest IP owners, ports, or\nwire format.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'aim', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: - 'Total bytes transferred in both directions.\n\nIf `source.bytes` and `destination.bytes` are known, `network.bytes` is their\nsum.', - example: 368, - }, - { - name: 'community_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash of source and destination IPs and ports, as well as the\nprotocol used in a communication. This is a tool-agnostic standard to identify\nflows.\n\nLearn more at https://github.com/corelight/community-id-spec.', - example: '1:hO+sN4H+MG5MY/8hIrXPqc4ZQz0=', - }, - { - name: 'direction', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - "Direction of the network traffic.\nRecommended values are:\n * inbound\n * outbound\n * internal\n * external\n * unknown\n\nWhen mapping events from a host-based monitoring context, populate this field from the host's point of view.\nWhen mapping events from a network or perimeter-based monitoring context, populate this field from the point of view of your network perimeter.", - example: 'inbound', - }, - { - name: 'forwarded_ip', - level: 'core', - type: 'ip', - description: 'Host IP address when the source IP address is the proxy.', - example: '192.1.1.2', - }, - { - name: 'iana_number', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'IANA Protocol Number (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml).\nStandardized list of protocols. This aligns well with NetFlow and sFlow related\nlogs which use the IANA Protocol Number.', - example: 6, - }, - { - name: 'inner', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'Network.inner fields are added in addition to network.vlan fields\nto describe the innermost VLAN when q-in-q VLAN tagging is present. Allowed\nfields include vlan.id and vlan.name. Inner vlan fields are typically used\nwhen sending traffic with multiple 802.1q encapsulations to a network sensor\n(e.g. Zeek, Wireshark.)', - default_field: false, - }, - { - name: 'inner.vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'inner.vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name given by operators to sections of their network.', - example: 'Guest Wifi', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: - 'Total packets transferred in both directions.\n\nIf `source.packets` and `destination.packets` are known, `network.packets`\nis their sum.', - example: 24, - }, - { - name: 'protocol', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'L7 Network protocol name. ex. http, lumberjack, transport protocol.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'http', - }, - { - name: 'transport', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Same as network.iana_number, but instead using the Keyword name\nof the transport layer (udp, tcp, ipv6-icmp, etc.)\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'tcp', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'In the OSI Model this would be the Network Layer. ipv4, ipv6,\nipsec, pim, etc\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'ipv4', - }, - { - name: 'vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - ], - }, - { - name: 'observer', - title: 'Observer', - group: 2, - description: - 'An observer is defined as a special network, security, or application\ndevice used to detect, observe, or create network, security, or application-related\nevents and metrics.\n\nThis could be a custom hardware appliance or a server that has been configured\nto run special network, security, or application software. Examples include\nfirewalls, web proxies, intrusion detection/prevention systems, network monitoring\nsensors, web application firewalls, data loss prevention systems, and APM servers.\nThe observer.* fields shall be populated with details of the system, if any,\nthat detects, observes and/or creates a network, security, or application event\nor metric. Message queues and ETL components used in processing events or metrics\nare not considered observers in ECS.', - type: 'group', - fields: [ - { - name: 'egress', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'Observer.egress holds information like interface number and name,\nvlan, and zone information to classify egress traffic. Single armed monitoring\nsuch as a network sensor on a span port should only use observer.ingress\nto categorize traffic.', - default_field: false, - }, - { - name: 'egress.interface.alias', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Interface alias as reported by the system, typically used in firewall\nimplementations for e.g. inside, outside, or dmz logical interface naming.', - example: 'outside', - default_field: false, - }, - { - name: 'egress.interface.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface ID as reported by an observer (typically SNMP interface\nID).', - example: 10, - default_field: false, - }, - { - name: 'egress.interface.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface name as reported by the system.', - example: 'eth0', - default_field: false, - }, - { - name: 'egress.vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'egress.vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - { - name: 'egress.zone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Network zone of outbound traffic as reported by the observer to\ncategorize the destination area of egress traffic, e.g. Internal, External,\nDMZ, HR, Legal, etc.', - example: 'Public_Internet', - default_field: false, - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'hostname', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Hostname of the observer.', - }, - { - name: 'ingress', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'Observer.ingress holds information like interface number and name,\nvlan, and zone information to classify ingress traffic. Single armed monitoring\nsuch as a network sensor on a span port should only use observer.ingress\nto categorize traffic.', - default_field: false, - }, - { - name: 'ingress.interface.alias', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Interface alias as reported by the system, typically used in firewall\nimplementations for e.g. inside, outside, or dmz logical interface naming.', - example: 'outside', - default_field: false, - }, - { - name: 'ingress.interface.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface ID as reported by an observer (typically SNMP interface\nID).', - example: 10, - default_field: false, - }, - { - name: 'ingress.interface.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface name as reported by the system.', - example: 'eth0', - default_field: false, - }, - { - name: 'ingress.vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'ingress.vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - { - name: 'ingress.zone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Network zone of incoming traffic as reported by the observer to\ncategorize the source area of ingress traffic. e.g. internal, External, DMZ,\nHR, Legal, etc.', - example: 'DMZ', - default_field: false, - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'IP addresses of the observer.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC addresses of the observer', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Custom name of the observer.\n\nThis is a name that can be given to an observer. This can be helpful for example\nif multiple firewalls of the same model are used in an organization.\n\nIf no custom name is needed, the field can be left empty.', - example: '1_proxySG', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The product name of the observer.', - example: 's200', - }, - { - name: 'serial_number', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Observer serial number.', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of the observer the data is coming from.\n\nThere is no predefined list of observer types. Some examples are `forwarder`,\n`firewall`, `ids`, `ips`, `proxy`, `poller`, `sensor`, `APM server`.', - example: 'firewall', - }, - { - name: 'vendor', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Vendor name of the observer.', - example: 'Symantec', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Observer version.', - }, - ], - }, - { - name: 'organization', - title: 'Organization', - group: 2, - description: - 'The organization fields enrich data with information about the company\nor entity the data is associated with.\n\nThese fields help you arrange or filter data stored in an index by one or multiple\norganizations.', - type: 'group', - fields: [ - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the organization.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - }, - ], - }, - { - name: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - type: 'group', - fields: [ - { - name: 'family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - ], - }, - { - name: 'package', - title: 'Package', - group: 2, - description: - 'These fields contain information about an installed software package.\nIt contains general information about a package, such as name, version or size.\nIt also contains installation details, such as time or location.', - type: 'group', - fields: [ - { - name: 'architecture', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Package architecture.', - example: 'x86_64', - }, - { - name: 'build_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the build version of the installed\npackage.\n\nFor example use the commit SHA of a non-released package.', - example: '36f4f7e89dd61b0988b12ee000b98966867710cd', - default_field: false, - }, - { - name: 'checksum', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Checksum of the installed package for verification.', - example: '68b329da9893e34099c7d8ad5cb9c940', - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Description of the package.', - example: - 'Open source programming language to build simple/reliable/efficient\nsoftware.', - }, - { - name: 'install_scope', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Indicating how the package was installed, e.g. user-local, global.', - example: 'global', - }, - { - name: 'installed', - level: 'extended', - type: 'date', - description: 'Time when package was installed.', - }, - { - name: 'license', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'License under which the package was released.\n\nUse a short name, e.g. the license identifier from SPDX License List where\npossible (https://spdx.org/licenses/).', - example: 'Apache License 2.0', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Package name', - example: 'go', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Path where the package is installed.', - example: '/usr/local/Cellar/go/1.12.9/', - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Home page or reference URL of the software in this package, if\navailable.', - example: 'https://golang.org', - default_field: false, - }, - { - name: 'size', - level: 'extended', - type: 'long', - format: 'string', - description: 'Package size in bytes.', - example: 62231, - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Type of package.\n\nThis should contain the package file type, rather than the package manager\nname. Examples: rpm, dpkg, brew, npm, gem, nupkg, jar.', - example: 'rpm', - default_field: false, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Package version', - example: '1.12.9', - }, - ], - }, - { - name: 'pe', - title: 'PE Header', - group: 2, - description: 'These fields contain Windows Portable Executable (PE) metadata.', - type: 'group', - fields: [ - { - name: 'company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - ], - }, - { - name: 'process', - title: 'Process', - group: 2, - description: - 'These fields contain information about a process.\n\nThese fields can help you correlate metrics information with a process id/name\nfrom a log message. The `process.pid` often stays in the metric itself and\nis copied to the global field for correlation.', - type: 'group', - fields: [ - { - name: 'args', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.', - example: ['/usr/bin/ssh', '-l', 'user', '10.0.0.16'], - }, - { - name: 'args_count', - level: 'extended', - type: 'long', - description: - 'Length of the process.args array.\n\nThis field can be useful for querying or performing bucket analysis on how\nmany arguments were provided to start a process. More arguments may be an\nindication of suspicious activity.', - example: 4, - default_field: false, - }, - { - name: 'code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'command_line', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'Full command line that started the process, including the absolute\npath to the executable, and all arguments.\n\nSome arguments may be filtered to protect sensitive information.', - example: '/usr/bin/ssh -l user 10.0.0.16', - default_field: false, - }, - { - name: 'entity_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier for the process.\n\nThe implementation of this is specified by the data source, but some examples\nof what could be used here are a process-generated UUID, Sysmon Process GUIDs,\nor a hash of some uniquely identifying components of a process.\n\nConstructing a globally unique identifier is a common practice to mitigate\nPID reuse as well as to identify a specific process over time, across multiple\nmonitored hosts.', - example: 'c2c455d9f99375d', - default_field: false, - }, - { - name: 'executable', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', - }, - { - name: 'exit_code', - level: 'extended', - type: 'long', - description: - 'The exit code of the process, if this is a termination event.\n\nThe field should be absent if there is no exit code for the event (e.g. process\nstart).', - example: 137, - default_field: false, - }, - { - name: 'hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - }, - { - name: 'hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - }, - { - name: 'hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - }, - { - name: 'hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Process name.\n\nSometimes called program name or similar.', - example: 'ssh', - }, - { - name: 'parent.args', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of process arguments.\n\nMay be filtered to protect sensitive information.', - example: ['ssh', '-l', 'user', '10.0.0.16'], - default_field: false, - }, - { - name: 'parent.args_count', - level: 'extended', - type: 'long', - description: - 'Length of the process.args array.\n\nThis field can be useful for querying or performing bucket analysis on how\nmany arguments were provided to start a process. More arguments may be an\nindication of suspicious activity.', - example: 4, - default_field: false, - }, - { - name: 'parent.code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'parent.code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'parent.code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'parent.code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'parent.code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'parent.command_line', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'Full command line that started the process, including the absolute\npath to the executable, and all arguments.\n\nSome arguments may be filtered to protect sensitive information.', - example: '/usr/bin/ssh -l user 10.0.0.16', - default_field: false, - }, - { - name: 'parent.entity_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier for the process.\n\nThe implementation of this is specified by the data source, but some examples\nof what could be used here are a process-generated UUID, Sysmon Process GUIDs,\nor a hash of some uniquely identifying components of a process.\n\nConstructing a globally unique identifier is a common practice to mitigate\nPID reuse as well as to identify a specific process over time, across multiple\nmonitored hosts.', - example: 'c2c455d9f99375d', - default_field: false, - }, - { - name: 'parent.executable', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', - default_field: false, - }, - { - name: 'parent.exit_code', - level: 'extended', - type: 'long', - description: - 'The exit code of the process, if this is a termination event.\n\nThe field should be absent if there is no exit code for the event (e.g. process\nstart).', - example: 137, - default_field: false, - }, - { - name: 'parent.hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - default_field: false, - }, - { - name: 'parent.hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - default_field: false, - }, - { - name: 'parent.hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - default_field: false, - }, - { - name: 'parent.hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - default_field: false, - }, - { - name: 'parent.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'Process name.\n\nSometimes called program name or similar.', - example: 'ssh', - default_field: false, - }, - { - name: 'parent.pgid', - level: 'extended', - type: 'long', - format: 'string', - description: 'Identifier of the group of processes the process belongs to.', - default_field: false, - }, - { - name: 'parent.pid', - level: 'core', - type: 'long', - format: 'string', - description: 'Process id.', - example: 4242, - default_field: false, - }, - { - name: 'parent.ppid', - level: 'extended', - type: 'long', - format: 'string', - description: "Parent process' pid.", - example: 4241, - default_field: false, - }, - { - name: 'parent.start', - level: 'extended', - type: 'date', - description: 'The time the process started.', - example: '2016-05-23T08:05:34.853Z', - default_field: false, - }, - { - name: 'parent.thread.id', - level: 'extended', - type: 'long', - format: 'string', - description: 'Thread ID.', - example: 4242, - default_field: false, - }, - { - name: 'parent.thread.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Thread name.', - example: 'thread-0', - default_field: false, - }, - { - name: 'parent.title', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'Process title.\n\nThe proctitle, some times the same as process name. Can also be different:\nfor example a browser setting its title to the web page currently opened.', - default_field: false, - }, - { - name: 'parent.uptime', - level: 'extended', - type: 'long', - description: 'Seconds the process has been up.', - example: 1325, - default_field: false, - }, - { - name: 'parent.working_directory', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'The working directory of the process.', - example: '/home/alice', - default_field: false, - }, - { - name: 'pe.company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'pe.description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'pe.file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'pe.original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'pe.product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - { - name: 'pgid', - level: 'extended', - type: 'long', - format: 'string', - description: 'Identifier of the group of processes the process belongs to.', - }, - { - name: 'pid', - level: 'core', - type: 'long', - format: 'string', - description: 'Process id.', - example: 4242, - }, - { - name: 'ppid', - level: 'extended', - type: 'long', - format: 'string', - description: "Parent process' pid.", - example: 4241, - }, - { - name: 'start', - level: 'extended', - type: 'date', - description: 'The time the process started.', - example: '2016-05-23T08:05:34.853Z', - }, - { - name: 'thread.id', - level: 'extended', - type: 'long', - format: 'string', - description: 'Thread ID.', - example: 4242, - }, - { - name: 'thread.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Thread name.', - example: 'thread-0', - }, - { - name: 'title', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'Process title.\n\nThe proctitle, some times the same as process name. Can also be different:\nfor example a browser setting its title to the web page currently opened.', - }, - { - name: 'uptime', - level: 'extended', - type: 'long', - description: 'Seconds the process has been up.', - example: 1325, - }, - { - name: 'working_directory', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The working directory of the process.', - example: '/home/alice', - }, - ], - }, - { - name: 'registry', - title: 'Registry', - group: 2, - description: 'Fields related to Windows Registry operations.', - type: 'group', - fields: [ - { - name: 'data.bytes', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Original bytes written with base64 encoding.\n\nFor Windows registry operations, such as SetValueEx and RegQueryValueEx, this\ncorresponds to the data pointed by `lp_data`. This is optional but provides\nbetter recoverability and should be populated for REG_BINARY encoded values.', - example: 'ZQBuAC0AVQBTAAAAZQBuAAAAAAA=', - default_field: false, - }, - { - name: 'data.strings', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Content when writing string types.\n\nPopulated as an array when writing string data to the registry. For single\nstring registry types (REG_SZ, REG_EXPAND_SZ), this should be an array with\none string. For sequences of string with REG_MULTI_SZ, this array will be\nvariable length. For numeric data, such as REG_DWORD and REG_QWORD, this should\nbe populated with the decimal representation (e.g `"1"`).', - example: '["C:\\rta\\red_ttp\\bin\\myapp.exe"]', - default_field: false, - }, - { - name: 'data.type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Standard registry type for encoding contents', - example: 'REG_SZ', - default_field: false, - }, - { - name: 'hive', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Abbreviated name for the hive.', - example: 'HKLM', - default_field: false, - }, - { - name: 'key', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Hive-relative path of keys.', - example: - 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\winword.exe', - default_field: false, - }, - { - name: 'path', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Full path, including hive, key and value', - example: - 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution\nOptions\\winword.exe\\Debugger', - default_field: false, - }, - { - name: 'value', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the value written.', - example: 'Debugger', - default_field: false, - }, - ], - }, - { - name: 'related', - title: 'Related', - group: 2, - description: - 'This field set is meant to facilitate pivoting around a piece of\ndata.\n\nSome pieces of information can be seen in many places in an ECS event. To facilitate\nsearching for them, store an array of all seen values to their corresponding\nfield in `related.`.\n\nA concrete example is IP addresses, which can be under host, observer, source,\ndestination, client, server, and network.forwarded_ip. If you append all IPs\nto `related.ip`, you can then search for a given IP trivially, no matter where\nit appeared, by querying `related.ip:192.0.2.15`.', - type: 'group', - fields: [ - { - name: 'hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - "All the hashes seen on your event. Populating this field, then\nusing it to search for hashes can help in situations where you're unsure what\nthe hash algorithm is (and therefore which key name to search).", - default_field: false, - }, - { - name: 'ip', - level: 'extended', - type: 'ip', - description: 'All of the IPs seen on your event.', - }, - { - name: 'user', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'All the user names seen on your event.', - default_field: false, - }, - ], - }, - { - name: 'rule', - title: 'Rule', - group: 2, - description: - 'Rule fields are used to capture the specifics of any observer or\nagent rules that generate alerts or other notable events.\n\nExamples of data sources that would populate the rule fields include: network\nadmission control platforms, network or host IDS/IPS, network firewalls, web\napplication firewalls, url filters, endpoint detection and response (EDR) systems,\netc.', - type: 'group', - fields: [ - { - name: 'author', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name, organization, or pseudonym of the author or authors who created\nthe rule used to generate this event.', - example: ['Star-Lord'], - default_field: false, - }, - { - name: 'category', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A categorization value keyword used by the entity using the rule\nfor detection of this event.', - example: 'Attempted Information Leak', - default_field: false, - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The description of the rule generating the event.', - example: 'Block requests to public DNS over HTTPS / TLS protocols', - default_field: false, - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A rule ID that is unique within the scope of an agent, observer,\nor other entity using the rule for detection of this event.', - example: 101, - default_field: false, - }, - { - name: 'license', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the license under which the rule used to generate this\nevent is made available.', - example: 'Apache 2.0', - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The name of the rule or signature generating the event.', - example: 'BLOCK_DNS_over_TLS', - default_field: false, - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Reference URL to additional information about the rule used to\ngenerate this event.\n\nThe URL can point to the vendor documentation about the rule. If that is\nnot available, it can also be a link to a more general page describing this\ntype of alert.', - example: 'https://en.wikipedia.org/wiki/DNS_over_TLS', - default_field: false, - }, - { - name: 'ruleset', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the ruleset, policy, group, or parent category in which\nthe rule used to generate this event is a member.', - example: 'Standard_Protocol_Filters', - default_field: false, - }, - { - name: 'uuid', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A rule ID that is unique within the scope of a set or group of\nagents, observers, or other entities using the rule for detection of this\nevent.', - example: 1100110011, - default_field: false, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The version / revision of the rule being used for analysis.', - example: 1.1, - default_field: false, - }, - ], - }, - { - name: 'server', - title: 'Server', - group: 2, - description: - 'A Server is defined as the responder in a network connection for\nevents regarding sessions, connections, or bidirectional flow records.\n\nFor TCP events, the server is the receiver of the initial SYN packet(s) of the\nTCP connection. For other protocols, the server is generally the responder in\nthe network transaction. Some systems actually use the term "responder" to refer\nthe server in TCP connections. The server fields describe details about the\nsystem acting as the server in the network event. Server fields are usually\npopulated in conjunction with client fields. Server fields are generally not\npopulated for packet-level events.\n\nClient / server representations can add semantic context to an exchange, which\nis helpful to visualize the data in certain situations. If your context falls\nin that category, you should still ensure that source and destination are filled\nappropriately.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event server addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the server to the client.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Server domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the server.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the server.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated ip of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Translated port of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the server to the client.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the server.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered server domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'service', - title: 'Service', - group: 2, - description: - 'The service fields describe the service for or from which the data\nwas collected.\n\nThese fields help you find and correlate logs for a specific service and version.', - type: 'group', - fields: [ - { - name: 'ephemeral_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Ephemeral identifier of this service (if one exists).\n\nThis id normally changes across restarts, but `service.id` does not.', - example: '8a4f500f', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of the running service. If the service is comprised\nof many nodes, the `service.id` should be the same for all nodes.\n\nThis id should uniquely identify the service. This makes it possible to correlate\nlogs and metrics for one specific service, no matter which particular node\nemitted the event.\n\nNote that if you need to see the events from one specific host of the service,\nyou should filter on that `host.name` or `host.id` instead.', - example: 'd37e5ebfe0ae6c4972dbe9f0174a1637bb8247f6', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the service data is collected from.\n\nThe name of the service is normally user given. This allows for distributed\nservices that run on multiple hosts to correlate the related instances based\non the name.\n\nIn the case of Elasticsearch the `service.name` could contain the cluster\nname. For Beats the `service.name` is by default a copy of the `service.type`\nfield if no name is specified.', - example: 'elasticsearch-metrics', - }, - { - name: 'node.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of a service node.\n\nThis allows for two nodes of the same service running on the same host to\nbe differentiated. Therefore, `service.node.name` should typically be unique\nacross nodes of a given service.\n\nIn the case of Elasticsearch, the `service.node.name` could contain the unique\nnode name within the Elasticsearch cluster. In cases where the service does not\nhave the concept of a node name, the host name or container name can be used\nto distinguish running instances that make up this service. If those do not\nprovide uniqueness (e.g. multiple instances of the service running on the\nsame host) - the node name can be manually set.', - example: 'instance-0000000016', - }, - { - name: 'state', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Current state of the service.', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of the service data is collected from.\n\nThe type can be used to group and correlate logs and metrics from one service\ntype.\n\nExample: If logs or metrics are collected from Elasticsearch, `service.type`\nwould be `elasticsearch`.', - example: 'elasticsearch', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Version of the service the data was collected from.\n\nThis allows to look at a data set only for a specific version of a service.', - example: '3.2.4', - }, - ], - }, - { - name: 'source', - title: 'Source', - group: 2, - description: - 'Source fields describe details about the source of a packet/event.\n\nSource fields are usually populated in conjunction with destination fields.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event source addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the source to the destination.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Source domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the source.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the source.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated ip of source based NAT sessions (e.g. internal client\nto internet)\n\nTypically connections traversing load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Translated port of source based NAT sessions. (e.g. internal client\nto internet)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the source to the destination.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the source.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered source domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'threat', - title: 'Threat', - group: 2, - description: - 'Fields to classify events and alerts according to a threat taxonomy\nsuch as the Mitre ATT&CK framework.\n\nThese fields are for users to classify alerts from all of their sources (e.g.\nIDS, NGFW, etc.) within a common taxonomy. The threat.tactic.* are meant to\ncapture the high level category of the threat (e.g. "impact"). The threat.technique.*\nfields are meant to capture which kind of approach is used by this detected\nthreat, to accomplish the goal (e.g. "endpoint denial of service").', - type: 'group', - fields: [ - { - name: 'framework', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the threat framework used to further categorize and classify\nthe tactic and technique of the reported threat. Framework classification\ncan be provided by detecting systems, evaluated at ingest time, or retrospectively\ntagged to events.', - example: 'MITRE ATT&CK', - }, - { - name: 'tactic.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The id of tactic used by this threat. You can use the Mitre ATT&CK\nMatrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/\n)', - example: 'TA0040', - }, - { - name: 'tactic.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the type of tactic used by this threat. You can use the\nMitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/\n)', - example: 'impact', - }, - { - name: 'tactic.reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The reference url of tactic used by this threat. You can use the\nMitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/\n)', - example: 'https://attack.mitre.org/tactics/TA0040/', - }, - { - name: 'technique.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The id of technique used by this tactic. You can use the Mitre\nATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/\n)', - example: 'T1499', - }, - { - name: 'technique.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'The name of technique used by this tactic. You can use the Mitre\nATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/\n)', - example: 'endpoint denial of service', - }, - { - name: 'technique.reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The reference url of technique used by this tactic. You can use\nthe Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/\n)', - example: 'https://attack.mitre.org/techniques/T1499/', - }, - ], - }, - { - name: 'tls', - title: 'TLS', - group: 2, - description: - 'Fields related to a TLS connection. These fields focus on the TLS\nprotocol itself and intentionally avoids in-depth analysis of the related x.509\ncertificate files.', - type: 'group', - fields: [ - { - name: 'cipher', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'String indicating the cipher used during the current connection.', - example: 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256', - default_field: false, - }, - { - name: 'client.certificate', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'PEM-encoded stand-alone certificate offered by the client. This\nis usually mutually-exclusive of `client.certificate_chain` since this value\nalso exists in that list.', - example: 'MII...', - default_field: false, - }, - { - name: 'client.certificate_chain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of PEM-encoded certificates that make up the certificate\nchain offered by the client. This is usually mutually-exclusive of `client.certificate`\nsince that value should be the first certificate in the chain.', - example: ['MII...', 'MII...'], - default_field: false, - }, - { - name: 'client.hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the MD5 digest of DER-encoded version\nof certificate offered by the client. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC', - default_field: false, - }, - { - name: 'client.hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA1 digest of DER-encoded version\nof certificate offered by the client. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '9E393D93138888D288266C2D915214D1D1CCEB2A', - default_field: false, - }, - { - name: 'client.hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA256 digest of DER-encoded\nversion of certificate offered by the client. For consistency with other hash\nvalues, this value should be formatted as an uppercase hash.', - example: '0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0', - default_field: false, - }, - { - name: 'client.issuer', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Distinguished name of subject of the issuer of the x.509 certificate\npresented by the client.', - example: 'CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'client.ja3', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash that identifies clients based on how they perform an SSL/TLS\nhandshake.', - example: 'd4e5b18d6b55c71272893221c96ba240', - default_field: false, - }, - { - name: 'client.not_after', - level: 'extended', - type: 'date', - description: - 'Date/Time indicating when client certificate is no longer considered\nvalid.', - example: '2021-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'client.not_before', - level: 'extended', - type: 'date', - description: 'Date/Time indicating when client certificate is first considered\nvalid.', - example: '1970-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'client.server_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Also called an SNI, this tells the server which hostname to which\nthe client is attempting to connect. When this value is available, it should\nget copied to `destination.domain`.', - example: 'www.elastic.co', - default_field: false, - }, - { - name: 'client.subject', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Distinguished name of subject of the x.509 certificate presented\nby the client.', - example: 'CN=myclient, OU=Documentation Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'client.supported_ciphers', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Array of ciphers offered by the client during the client hello.', - example: [ - 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384', - 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384', - '...', - ], - default_field: false, - }, - { - name: 'curve', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'String indicating the curve used for the given cipher, when applicable.', - example: 'secp256r1', - default_field: false, - }, - { - name: 'established', - level: 'extended', - type: 'boolean', - description: - 'Boolean flag indicating if the TLS negotiation was successful and\ntransitioned to an encrypted tunnel.', - default_field: false, - }, - { - name: 'next_protocol', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'String indicating the protocol being tunneled. Per the values in\nthe IANA registry (https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids),\nthis string should be lower case.', - example: 'http/1.1', - default_field: false, - }, - { - name: 'resumed', - level: 'extended', - type: 'boolean', - description: - 'Boolean flag indicating if this TLS connection was resumed from\nan existing TLS negotiation.', - default_field: false, - }, - { - name: 'server.certificate', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'PEM-encoded stand-alone certificate offered by the server. This\nis usually mutually-exclusive of `server.certificate_chain` since this value\nalso exists in that list.', - example: 'MII...', - default_field: false, - }, - { - name: 'server.certificate_chain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of PEM-encoded certificates that make up the certificate\nchain offered by the server. This is usually mutually-exclusive of `server.certificate`\nsince that value should be the first certificate in the chain.', - example: ['MII...', 'MII...'], - default_field: false, - }, - { - name: 'server.hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the MD5 digest of DER-encoded version\nof certificate offered by the server. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC', - default_field: false, - }, - { - name: 'server.hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA1 digest of DER-encoded version\nof certificate offered by the server. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '9E393D93138888D288266C2D915214D1D1CCEB2A', - default_field: false, - }, - { - name: 'server.hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA256 digest of DER-encoded\nversion of certificate offered by the server. For consistency with other hash\nvalues, this value should be formatted as an uppercase hash.', - example: '0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0', - default_field: false, - }, - { - name: 'server.issuer', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Subject of the issuer of the x.509 certificate presented by the\nserver.', - example: 'CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'server.ja3s', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash that identifies servers based on how they perform an SSL/TLS\nhandshake.', - example: '394441ab65754e2207b1e1b457b3641d', - default_field: false, - }, - { - name: 'server.not_after', - level: 'extended', - type: 'date', - description: - 'Timestamp indicating when server certificate is no longer considered\nvalid.', - example: '2021-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'server.not_before', - level: 'extended', - type: 'date', - description: 'Timestamp indicating when server certificate is first considered\nvalid.', - example: '1970-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'server.subject', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Subject of the x.509 certificate presented by the server.', - example: 'CN=www.mydomain.com, OU=Infrastructure Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Numeric part of the version parsed from the original string.', - example: '1.2', - default_field: false, - }, - { - name: 'version_protocol', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Normalized lowercase protocol name parsed from original string.', - example: 'tls', - default_field: false, - }, - ], - }, - { - name: 'tracing', - title: 'Tracing', - group: 2, - description: - 'Distributed tracing makes it possible to analyze performance throughout\na microservice architecture all in one view. This is accomplished by tracing\nall of the requests - from the initial web request in the front-end service\n- to queries made through multiple back-end services.', - type: 'group', - fields: [ - { - name: 'trace.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of the trace.\n\nA trace groups multiple events like transactions that belong together. For\nexample, a user request handled by multiple inter-connected services.', - example: '4bf92f3577b34da6a3ce929d0e0e4736', - }, - { - name: 'transaction.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of the transaction.\n\nA transaction is the highest level of work measured within a service, such\nas a request to a server.', - example: '00f067aa0ba902b7', - }, - ], - }, - { - name: 'url', - title: 'URL', - group: 2, - description: - 'URL fields provide support for complete or partial URLs, and supports\nthe breaking down into scheme, domain, path, and so on.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Domain of the url, such as "www.elastic.co".\n\nIn some cases a URL may refer to an IP and/or port directly, without a domain\nname. In this case, the IP address would go to the `domain` field.', - example: 'www.elastic.co', - }, - { - name: 'extension', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The field contains the file extension from the original request\nurl.\n\nThe file extension is only set if it exists, as not every url has a file extension.\n\nThe leading period must not be included. For example, the value must be "png",\nnot ".png".', - example: 'png', - }, - { - name: 'fragment', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Portion of the url after the `#`, such as "top".\n\nThe `#` is not part of the fragment.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'If full URLs are important to your use case, they should be stored\nin `url.full`, whether this field is reconstructed or present in the event\nsource.', - example: 'https://www.elastic.co:443/search?q=elasticsearch#top', - }, - { - name: 'original', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'Unmodified original url as seen in the event source.\n\nNote that in network monitoring, the observed URL may be a full URL, whereas\nin access logs, the URL is often just represented as a path.\n\nThis field is meant to represent the URL as it was observed, complete or not.', - example: - 'https://www.elastic.co:443/search?q=elasticsearch#top or /search?q=elasticsearch', - }, - { - name: 'password', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Password of the request.', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Path of the request, such as "/search".', - }, - { - name: 'port', - level: 'extended', - type: 'long', - format: 'string', - description: 'Port of the request, such as 443.', - example: 443, - }, - { - name: 'query', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The query field describes the query string of the request, such\nas "q=elasticsearch".\n\nThe `?` is excluded from the query string. If a URL contains no `?`, there\nis no query field. If there is a `?` but no query, the query field exists\nwith an empty string. The `exists` query can be used to differentiate between\nthe two cases.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered url domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'scheme', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Scheme of the request, such as "https".\n\nNote: The `:` is not part of the scheme.', - example: 'https', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'username', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Username of the request.', - }, - ], - }, - { - name: 'user', - title: 'User', - group: 2, - description: - 'The user fields describe information about the user that is relevant\nto the event.\n\nFields can have one entry or multiple entries. If a user has more than one id,\nprovide an array that includes all of them.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'user_agent', - title: 'User agent', - group: 2, - description: - 'The user_agent fields normally come from a browser request.\n\nThey often show up in web service logs coming from the parsed user agent string.', - type: 'group', - fields: [ - { - name: 'device.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the device.', - example: 'iPhone', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the user agent.', - example: 'Safari', - }, - { - name: 'original', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'Unparsed user_agent string.', - example: - 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15\n(KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Version of the user agent.', - example: 12, - }, - ], - }, - { - name: 'vlan', - title: 'VLAN', - group: 2, - description: - 'The VLAN fields are used to identify 802.1q tag(s) of a packet,\nas well as ingress and egress VLAN associations of an observer in relation to\na specific packet or connection.\n\nNetwork.vlan fields are used to record a single VLAN tag, or the outer tag in\nthe case of q-in-q encapsulations, for a packet or connection as observed, typically\nprovided by a network sensor (e.g. Zeek, Wireshark) passively reporting on traffic.\n\nNetwork.inner VLAN fields are used to report inner q-in-q 802.1q tags (multiple\n802.1q encapsulations) as observed, typically provided by a network sensor (e.g.\nZeek, Wireshark) passively reporting on traffic. Network.inner VLAN fields should\nonly be used in addition to network.vlan fields to indicate q-in-q tagging.\n\nObserver.ingress and observer.egress VLAN values are used to record observer\nspecific information when observer events contain discrete ingress and egress\nVLAN information, typically provided by firewalls, routers, or load balancers.', - type: 'group', - fields: [ - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - ], - }, - { - name: 'vulnerability', - title: 'Vulnerability', - group: 2, - description: - 'The vulnerability fields describe information about a vulnerability\nthat is relevant to an event.', - type: 'group', - fields: [ - { - name: 'category', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of system or architecture that the vulnerability affects.\nThese may be platform-specific (for example, Debian or SUSE) or general (for\nexample, Database or Firewall). For example (https://qualysguard.qualys.com/qwebhelp/fo_portal/knowledgebase/vulnerability_categories.htm[Qualys\nvulnerability categories])\n\nThis field must be an array.', - example: '["Firewall"]', - default_field: false, - }, - { - name: 'classification', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The classification of the vulnerability scoring system. For example\n(https://www.first.org/cvss/)', - example: 'CVSS', - default_field: false, - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'The description of the vulnerability that provides additional context\nof the vulnerability. For example (https://cve.mitre.org/about/faqs.html#cve_entry_descriptions_created[Common\nVulnerabilities and Exposure CVE description])', - example: 'In macOS before 2.12.6, there is a vulnerability in the RPC...', - default_field: false, - }, - { - name: 'enumeration', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of identifier used for this vulnerability. For example\n(https://cve.mitre.org/about/)', - example: 'CVE', - default_field: false, - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The identification (ID) is the number portion of a vulnerability\nentry. It includes a unique identification number for the vulnerability. For\nexample (https://cve.mitre.org/about/faqs.html#what_is_cve_id)[Common Vulnerabilities\nand Exposure CVE ID]', - example: 'CVE-2019-00001', - default_field: false, - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A resource that provides additional information, context, and mitigations\nfor the identified vulnerability.', - example: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6111', - default_field: false, - }, - { - name: 'report_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The report or scan identification number.', - example: 20191018.0001, - default_field: false, - }, - { - name: 'scanner.vendor', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The name of the vulnerability scanner vendor.', - example: 'Tenable', - default_field: false, - }, - { - name: 'score.base', - level: 'extended', - type: 'float', - description: - 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe.\n\nBase scores cover an assessment for exploitability metrics (attack vector,\ncomplexity, privileges, and user interaction), impact metrics (confidentiality,\nintegrity, and availability), and scope. For example (https://www.first.org/cvss/specification-document)', - example: 5.5, - default_field: false, - }, - { - name: 'score.environmental', - level: 'extended', - type: 'float', - description: - 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe.\n\nEnvironmental scores cover an assessment for any modified Base metrics, confidentiality,\nintegrity, and availability requirements. For example (https://www.first.org/cvss/specification-document)', - example: 5.5, - default_field: false, - }, - { - name: 'score.temporal', - level: 'extended', - type: 'float', - description: - 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe.\n\nTemporal scores cover an assessment for code maturity, remediation level,\nand confidence. For example (https://www.first.org/cvss/specification-document)', - default_field: false, - }, - { - name: 'score.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The National Vulnerability Database (NVD) provides qualitative\nseverity rankings of "Low", "Medium", and "High" for CVSS v2.0 base score\nranges in addition to the severity ratings for CVSS v3.0 as they are defined\nin the CVSS v3.0 specification.\n\nCVSS is owned and managed by FIRST.Org, Inc. (FIRST), a US-based non-profit\norganization, whose mission is to help computer security incident response\nteams across the world. For example (https://nvd.nist.gov/vuln-metrics/cvss)', - example: 2, - default_field: false, - }, - { - name: 'severity', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The severity of the vulnerability can help with metrics and internal\nprioritization regarding remediation. For example (https://nvd.nist.gov/vuln-metrics/cvss)', - example: 'Critical', - default_field: false, - }, - ], - }, - ], - }, - { - key: 'beat', - anchor: 'beat-common', - title: 'Beat', - description: 'Contains common beat fields available in all event types.\n', - fields: [ - { - name: 'agent.hostname', - type: 'keyword', - description: 'Hostname of the agent.', - }, - { - name: 'beat.timezone', - type: 'alias', - path: 'event.timezone', - migration: true, - }, - { - name: 'fields', - type: 'object', - object_type: 'keyword', - description: 'Contains user configurable fields.\n', - }, - { - name: 'beat.name', - type: 'alias', - path: 'host.name', - migration: true, - }, - { - name: 'beat.hostname', - type: 'alias', - path: 'agent.hostname', - migration: true, - }, - { - name: 'timeseries.instance', - type: 'keyword', - description: 'Time series instance id', - }, - ], - }, - { - key: 'cloud', - title: 'Cloud provider metadata', - description: 'Metadata from cloud providers added by the add_cloud_metadata processor.\n', - fields: [ - { - name: 'cloud.project.id', - example: 'project-x', - description: 'Name of the project in Google Cloud.\n', - }, - { - name: 'cloud.image.id', - example: 'ami-abcd1234', - description: 'Image ID for the cloud instance.\n', - }, - { - name: 'meta.cloud.provider', - type: 'alias', - path: 'cloud.provider', - migration: true, - }, - { - name: 'meta.cloud.instance_id', - type: 'alias', - path: 'cloud.instance.id', - migration: true, - }, - { - name: 'meta.cloud.instance_name', - type: 'alias', - path: 'cloud.instance.name', - migration: true, - }, - { - name: 'meta.cloud.machine_type', - type: 'alias', - path: 'cloud.machine.type', - migration: true, - }, - { - name: 'meta.cloud.availability_zone', - type: 'alias', - path: 'cloud.availability_zone', - migration: true, - }, - { - name: 'meta.cloud.project_id', - type: 'alias', - path: 'cloud.project.id', - migration: true, - }, - { - name: 'meta.cloud.region', - type: 'alias', - path: 'cloud.region', - migration: true, - }, - ], - }, - { - key: 'docker', - title: 'Docker', - description: 'Docker stats collected from Docker.\n', - short_config: false, - anchor: 'docker-processor', - fields: [ - { - name: 'docker', - type: 'group', - fields: [ - { - name: 'container.id', - type: 'alias', - path: 'container.id', - migration: true, - }, - { - name: 'container.image', - type: 'alias', - path: 'container.image.name', - migration: true, - }, - { - name: 'container.name', - type: 'alias', - path: 'container.name', - migration: true, - }, - { - name: 'container.labels', - type: 'object', - object_type: 'keyword', - description: 'Image labels.\n', - }, - ], - }, - ], - }, - { - key: 'host', - title: 'Host', - description: 'Info collected for the host machine.\n', - anchor: 'host-processor', - fields: [ - { - name: 'host', - type: 'group', - fields: [ - { - name: 'containerized', - type: 'boolean', - description: 'If the host is a container.\n', - }, - { - name: 'os.build', - type: 'keyword', - example: '18D109', - description: 'OS build information.\n', - }, - { - name: 'os.codename', - type: 'keyword', - example: 'stretch', - description: 'OS codename, if any.\n', - }, - ], - }, - ], - }, - { - key: 'kubernetes', - title: 'Kubernetes', - description: 'Kubernetes metadata added by the kubernetes processor\n', - short_config: false, - anchor: 'kubernetes-processor', - fields: [ - { - name: 'kubernetes', - type: 'group', - fields: [ - { - name: 'pod.name', - type: 'keyword', - description: 'Kubernetes pod name\n', - }, - { - name: 'pod.uid', - type: 'keyword', - description: 'Kubernetes Pod UID\n', - }, - { - name: 'namespace', - type: 'keyword', - description: 'Kubernetes namespace\n', - }, - { - name: 'node.name', - type: 'keyword', - description: 'Kubernetes node name\n', - }, - { - name: 'labels.*', - type: 'object', - object_type: 'keyword', - object_type_mapping_type: '*', - description: 'Kubernetes labels map\n', - }, - { - name: 'annotations.*', - type: 'object', - object_type: 'keyword', - object_type_mapping_type: '*', - description: 'Kubernetes annotations map\n', - }, - { - name: 'replicaset.name', - type: 'keyword', - description: 'Kubernetes replicaset name\n', - }, - { - name: 'deployment.name', - type: 'keyword', - description: 'Kubernetes deployment name\n', - }, - { - name: 'statefulset.name', - type: 'keyword', - description: 'Kubernetes statefulset name\n', - }, - { - name: 'container.name', - type: 'keyword', - description: 'Kubernetes container name\n', - }, - { - name: 'container.image', - type: 'keyword', - description: 'Kubernetes container image\n', - }, - ], - }, - ], - }, - { - key: 'process', - title: 'Process', - description: 'Process metadata fields\n', - fields: [ - { - name: 'process', - type: 'group', - fields: [ - { - name: 'exe', - type: 'alias', - path: 'process.executable', - migration: true, - }, - ], - }, - ], - }, - { - key: 'jolokia-autodiscover', - title: 'Jolokia Discovery autodiscover provider', - description: 'Metadata from Jolokia Discovery added by the jolokia provider.\n', - fields: [ - { - name: 'jolokia.agent.version', - type: 'keyword', - description: 'Version number of jolokia agent.\n', - }, - { - name: 'jolokia.agent.id', - type: 'keyword', - description: - 'Each agent has a unique id which can be either provided during startup of the agent in form of a configuration parameter or being autodetected. If autodected, the id has several parts: The IP, the process id, hashcode of the agent and its type.\n', - }, - { - name: 'jolokia.server.product', - type: 'keyword', - description: 'The container product if detected.\n', - }, - { - name: 'jolokia.server.version', - type: 'keyword', - description: "The container's version (if detected).\n", - }, - { - name: 'jolokia.server.vendor', - type: 'keyword', - description: 'The vendor of the container the agent is running in.\n', - }, - { - name: 'jolokia.url', - type: 'keyword', - description: 'The URL how this agent can be contacted.\n', - }, - { - name: 'jolokia.secured', - type: 'boolean', - description: 'Whether the agent was configured for authentication or not.\n', - }, - ], - }, - { - key: 'common', - title: 'Common', - description: 'Contains common fields available in all event types.\n', - fields: [ - { - name: 'file', - type: 'group', - description: 'File attributes.', - fields: [ - { - name: 'setuid', - type: 'boolean', - example: true, - description: 'Set if the file has the `setuid` bit set. Omitted otherwise.', - }, - { - name: 'setgid', - type: 'boolean', - example: true, - description: 'Set if the file has the `setgid` bit set. Omitted otherwise.', - }, - { - name: 'origin', - type: 'keyword', - description: - 'An array of strings describing a possible external origin for this file. For example, the URL it was downloaded from. Only supported in macOS, via the kMDItemWhereFroms attribute. Omitted if origin information is not available.\n', - multi_fields: [ - { - name: 'raw', - type: 'keyword', - description: - 'This is a non-analyzed field that is useful for aggregations on the origin data.\n', - }, - ], - }, - { - name: 'selinux', - type: 'group', - description: 'The SELinux identity of the file.', - fields: [ - { - name: 'user', - type: 'keyword', - description: 'The owner of the object.', - }, - { - name: 'role', - type: 'keyword', - description: "The object's SELinux role.", - }, - { - name: 'domain', - type: 'keyword', - description: "The object's SELinux domain or type.", - }, - { - name: 'level', - type: 'keyword', - example: 's0', - description: "The object's SELinux level.", - }, - ], - }, - ], - }, - { - name: 'user', - type: 'group', - description: 'User information.', - fields: [ - { - name: 'audit', - type: 'group', - description: 'Audit user information.', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'Audit user ID.', - }, - { - name: 'name', - type: 'keyword', - description: 'Audit user name.', - }, - ], - }, - { - name: 'effective', - type: 'group', - description: 'Effective user information.', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'Effective user ID.', - }, - { - name: 'name', - type: 'keyword', - description: 'Effective user name.', - }, - { - name: 'group', - type: 'group', - description: 'Effective group information.', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'Effective group ID.', - }, - { - name: 'name', - type: 'keyword', - description: 'Effective group name.', - }, - ], - }, - ], - }, - { - name: 'filesystem', - type: 'group', - description: 'Filesystem user information.', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'Filesystem user ID.', - }, - { - name: 'name', - type: 'keyword', - description: 'Filesystem user name.', - }, - { - name: 'group', - type: 'group', - description: 'Filesystem group information.', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'Filesystem group ID.', - }, - { - name: 'name', - type: 'keyword', - description: 'Filesystem group name.', - }, - ], - }, - ], - }, - { - name: 'saved', - type: 'group', - description: 'Saved user information.', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'Saved user ID.', - }, - { - name: 'name', - type: 'keyword', - description: 'Saved user name.', - }, - { - name: 'group', - type: 'group', - description: 'Saved group information.', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'Saved group ID.', - }, - { - name: 'name', - type: 'keyword', - description: 'Saved group name.', - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - key: 'auditd', - title: 'Auditd', - description: 'These are the fields generated by the auditd module.', - fields: [ - { - name: 'user', - type: 'group', - fields: [ - { - name: 'auid', - type: 'alias', - path: 'user.audit.id', - migration: true, - }, - { - name: 'uid', - type: 'alias', - path: 'user.id', - migration: true, - }, - { - name: 'euid', - type: 'alias', - path: 'user.effective.id', - migration: true, - }, - { - name: 'fsuid', - type: 'alias', - path: 'user.filesystem.id', - migration: true, - }, - { - name: 'suid', - type: 'alias', - path: 'user.saved.id', - migration: true, - }, - { - name: 'gid', - type: 'alias', - path: 'user.group.id', - migration: true, - }, - { - name: 'egid', - type: 'alias', - path: 'user.effective.group.id', - migration: true, - }, - { - name: 'sgid', - type: 'alias', - path: 'user.saved.group.id', - migration: true, - }, - { - name: 'fsgid', - type: 'alias', - path: 'user.filesystem.group.id', - migration: true, - }, - { - name: 'name_map', - type: 'group', - description: - 'If `resolve_ids` is set to true in the configuration then `name_map` will contain a mapping of uid field names to the resolved name (e.g. auid -> root).\n', - fields: [ - { - name: 'auid', - type: 'alias', - path: 'user.audit.name', - migration: true, - }, - { - name: 'uid', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'euid', - type: 'alias', - path: 'user.effective.name', - migration: true, - }, - { - name: 'fsuid', - type: 'alias', - path: 'user.filesystem.name', - migration: true, - }, - { - name: 'suid', - type: 'alias', - path: 'user.saved.name', - migration: true, - }, - { - name: 'gid', - type: 'alias', - path: 'user.group.name', - migration: true, - }, - { - name: 'egid', - type: 'alias', - path: 'user.effective.group.name', - migration: true, - }, - { - name: 'sgid', - type: 'alias', - path: 'user.saved.group.name', - migration: true, - }, - { - name: 'fsgid', - type: 'alias', - path: 'user.filesystem.group.name', - migration: true, - }, - ], - }, - { - name: 'selinux', - type: 'group', - description: 'The SELinux identity of the actor.', - fields: [ - { - name: 'user', - type: 'keyword', - description: 'account submitted for authentication', - }, - { - name: 'role', - type: 'keyword', - description: "user's SELinux role", - }, - { - name: 'domain', - type: 'keyword', - description: "The actor's SELinux domain or type.", - }, - { - name: 'level', - type: 'keyword', - example: 's0', - description: "The actor's SELinux level.", - }, - { - name: 'category', - type: 'keyword', - description: "The actor's SELinux category or compartments.", - }, - ], - }, - ], - }, - { - name: 'process', - type: 'group', - description: 'Process attributes.', - fields: [ - { - name: 'cwd', - type: 'alias', - path: 'process.working_directory', - migration: true, - description: 'The current working directory.', - }, - ], - }, - { - name: 'source', - type: 'group', - description: 'Source that triggered the event.', - fields: [ - { - name: 'path', - type: 'keyword', - description: 'This is the path associated with a unix socket.', - }, - ], - }, - { - name: 'destination', - type: 'group', - description: 'Destination address that triggered the event.', - fields: [ - { - name: 'path', - type: 'keyword', - description: 'This is the path associated with a unix socket.', - }, - ], - }, - { - name: 'auditd', - type: 'group', - fields: [ - { - name: 'message_type', - type: 'keyword', - example: 'syscall', - description: 'The audit message type (e.g. syscall or apparmor_denied).\n', - }, - { - name: 'sequence', - type: 'long', - description: - 'The sequence number of the event as assigned by the kernel. Sequence numbers are stored as a uint32 in the kernel and can rollover.\n', - }, - { - name: 'session', - type: 'keyword', - description: - 'The session ID assigned to a login. All events related to a login session will have the same value.\n', - }, - { - name: 'result', - type: 'keyword', - example: 'success or fail', - description: 'The result of the audited operation (success/fail).', - }, - { - name: 'summary', - type: 'group', - fields: [ - { - name: 'actor', - type: 'group', - description: 'The actor is the user that triggered the audit event.', - fields: [ - { - name: 'primary', - type: 'keyword', - description: - "The primary identity of the actor. This is the actor's original login ID. It will not change even if the user changes to another account.\n", - }, - { - name: 'secondary', - type: 'keyword', - description: - 'The secondary identity of the actor. This is typically\nthe same as the primary, except for when the user has used `su`.', - }, - ], - }, - { - name: 'object', - type: 'group', - description: 'This is the thing or object being acted upon in the event.\n', - fields: [ - { - name: 'type', - type: 'keyword', - description: - 'A description of the what the "thing" is (e.g. file, socket, user-session).\n', - }, - { - name: 'primary', - type: 'keyword', - description: '', - }, - { - name: 'secondary', - type: 'keyword', - description: '', - }, - ], - }, - { - name: 'how', - type: 'keyword', - description: - 'This describes how the action was performed. Usually this is the exe or command that was being executed that triggered the event.\n', - }, - ], - }, - { - name: 'paths', - type: 'group', - description: 'List of paths associated with the event.', - fields: [ - { - name: 'inode', - type: 'keyword', - description: 'inode number', - }, - { - name: 'dev', - type: 'keyword', - description: 'device name as found in /dev', - }, - { - name: 'obj_user', - type: 'keyword', - description: '', - }, - { - name: 'obj_role', - type: 'keyword', - description: '', - }, - { - name: 'obj_domain', - type: 'keyword', - description: '', - }, - { - name: 'obj_level', - type: 'keyword', - description: '', - }, - { - name: 'objtype', - type: 'keyword', - description: '', - }, - { - name: 'ouid', - type: 'keyword', - description: 'file owner user ID', - }, - { - name: 'rdev', - type: 'keyword', - description: 'the device identifier (special files only)', - }, - { - name: 'nametype', - type: 'keyword', - description: 'kind of file operation being referenced', - }, - { - name: 'ogid', - type: 'keyword', - description: 'file owner group ID', - }, - { - name: 'item', - type: 'keyword', - description: 'which item is being recorded', - }, - { - name: 'mode', - type: 'keyword', - description: 'mode flags on a file', - }, - { - name: 'name', - type: 'keyword', - description: 'file name in avcs', - }, - ], - }, - { - name: 'data', - type: 'group', - description: 'The data from the audit messages.', - fields: [ - { - name: 'action', - type: 'keyword', - description: 'netfilter packet disposition', - }, - { - name: 'minor', - type: 'keyword', - description: 'device minor number', - }, - { - name: 'acct', - type: 'keyword', - description: "a user's account name", - }, - { - name: 'addr', - type: 'keyword', - description: 'the remote address that the user is connecting from', - }, - { - name: 'cipher', - type: 'keyword', - description: 'name of crypto cipher selected', - }, - { - name: 'id', - type: 'keyword', - description: 'during account changes', - }, - { - name: 'entries', - type: 'keyword', - description: 'number of entries in the netfilter table', - }, - { - name: 'kind', - type: 'keyword', - description: 'server or client in crypto operation', - }, - { - name: 'ksize', - type: 'keyword', - description: 'key size for crypto operation', - }, - { - name: 'spid', - type: 'keyword', - description: 'sent process ID', - }, - { - name: 'arch', - type: 'keyword', - description: 'the elf architecture flags', - }, - { - name: 'argc', - type: 'keyword', - description: 'the number of arguments to an execve syscall', - }, - { - name: 'major', - type: 'keyword', - description: 'device major number', - }, - { - name: 'unit', - type: 'keyword', - description: 'systemd unit', - }, - { - name: 'table', - type: 'keyword', - description: 'netfilter table name', - }, - { - name: 'terminal', - type: 'keyword', - description: 'terminal name the user is running programs on', - }, - { - name: 'grantors', - type: 'keyword', - description: 'pam modules approving the action', - }, - { - name: 'direction', - type: 'keyword', - description: 'direction of crypto operation', - }, - { - name: 'op', - type: 'keyword', - description: 'the operation being performed that is audited', - }, - { - name: 'tty', - type: 'keyword', - description: 'tty udevice the user is running programs on', - }, - { - name: 'syscall', - type: 'keyword', - description: 'syscall number in effect when the event occurred', - }, - { - name: 'data', - type: 'keyword', - description: 'TTY text', - }, - { - name: 'family', - type: 'keyword', - description: 'netfilter protocol', - }, - { - name: 'mac', - type: 'keyword', - description: 'crypto MAC algorithm selected', - }, - { - name: 'pfs', - type: 'keyword', - description: 'perfect forward secrecy method', - }, - { - name: 'items', - type: 'keyword', - description: 'the number of path records in the event', - }, - { - name: 'a0', - type: 'keyword', - description: '', - }, - { - name: 'a1', - type: 'keyword', - description: '', - }, - { - name: 'a2', - type: 'keyword', - description: '', - }, - { - name: 'a3', - type: 'keyword', - description: '', - }, - { - name: 'hostname', - type: 'keyword', - description: 'the hostname that the user is connecting from', - }, - { - name: 'lport', - type: 'keyword', - description: 'local network port', - }, - { - name: 'rport', - type: 'keyword', - description: 'remote port number', - }, - { - name: 'exit', - type: 'keyword', - description: 'syscall exit code', - }, - { - name: 'fp', - type: 'keyword', - description: 'crypto key finger print', - }, - { - name: 'laddr', - type: 'keyword', - description: 'local network address', - }, - { - name: 'sport', - type: 'keyword', - description: 'local port number', - }, - { - name: 'capability', - type: 'keyword', - description: 'posix capabilities', - }, - { - name: 'nargs', - type: 'keyword', - description: 'the number of arguments to a socket call', - }, - { - name: 'new-enabled', - type: 'keyword', - description: 'new TTY audit enabled setting', - }, - { - name: 'audit_backlog_limit', - type: 'keyword', - description: "audit system's backlog queue size", - }, - { - name: 'dir', - type: 'keyword', - description: 'directory name', - }, - { - name: 'cap_pe', - type: 'keyword', - description: 'process effective capability map', - }, - { - name: 'model', - type: 'keyword', - description: 'security model being used for virt', - }, - { - name: 'new_pp', - type: 'keyword', - description: 'new process permitted capability map', - }, - { - name: 'old-enabled', - type: 'keyword', - description: 'present TTY audit enabled setting', - }, - { - name: 'oauid', - type: 'keyword', - description: "object's login user ID", - }, - { - name: 'old', - type: 'keyword', - description: 'old value', - }, - { - name: 'banners', - type: 'keyword', - description: 'banners used on printed page', - }, - { - name: 'feature', - type: 'keyword', - description: 'kernel feature being changed', - }, - { - name: 'vm-ctx', - type: 'keyword', - description: "the vm's context string", - }, - { - name: 'opid', - type: 'keyword', - description: "object's process ID", - }, - { - name: 'seperms', - type: 'keyword', - description: 'SELinux permissions being used', - }, - { - name: 'seresult', - type: 'keyword', - description: 'SELinux AVC decision granted/denied', - }, - { - name: 'new-rng', - type: 'keyword', - description: 'device name of rng being added from a vm', - }, - { - name: 'old-net', - type: 'keyword', - description: 'present MAC address assigned to vm', - }, - { - name: 'sigev_signo', - type: 'keyword', - description: 'signal number', - }, - { - name: 'ino', - type: 'keyword', - description: 'inode number', - }, - { - name: 'old_enforcing', - type: 'keyword', - description: 'old MAC enforcement status', - }, - { - name: 'old-vcpu', - type: 'keyword', - description: 'present number of CPU cores', - }, - { - name: 'range', - type: 'keyword', - description: "user's SE Linux range", - }, - { - name: 'res', - type: 'keyword', - description: 'result of the audited operation(success/fail)', - }, - { - name: 'added', - type: 'keyword', - description: 'number of new files detected', - }, - { - name: 'fam', - type: 'keyword', - description: 'socket address family', - }, - { - name: 'nlnk-pid', - type: 'keyword', - description: 'pid of netlink packet sender', - }, - { - name: 'subj', - type: 'keyword', - description: "lspp subject's context string", - }, - { - name: 'a[0-3]', - type: 'keyword', - description: 'the arguments to a syscall', - }, - { - name: 'cgroup', - type: 'keyword', - description: 'path to cgroup in sysfs', - }, - { - name: 'kernel', - type: 'keyword', - description: "kernel's version number", - }, - { - name: 'ocomm', - type: 'keyword', - description: "object's command line name", - }, - { - name: 'new-net', - type: 'keyword', - description: 'MAC address being assigned to vm', - }, - { - name: 'permissive', - type: 'keyword', - description: 'SELinux is in permissive mode', - }, - { - name: 'class', - type: 'keyword', - description: 'resource class assigned to vm', - }, - { - name: 'compat', - type: 'keyword', - description: 'is_compat_task result', - }, - { - name: 'fi', - type: 'keyword', - description: 'file assigned inherited capability map', - }, - { - name: 'changed', - type: 'keyword', - description: 'number of changed files', - }, - { - name: 'msg', - type: 'keyword', - description: 'the payload of the audit record', - }, - { - name: 'dport', - type: 'keyword', - description: 'remote port number', - }, - { - name: 'new-seuser', - type: 'keyword', - description: 'new SELinux user', - }, - { - name: 'invalid_context', - type: 'keyword', - description: 'SELinux context', - }, - { - name: 'dmac', - type: 'keyword', - description: 'remote MAC address', - }, - { - name: 'ipx-net', - type: 'keyword', - description: 'IPX network number', - }, - { - name: 'iuid', - type: 'keyword', - description: "ipc object's user ID", - }, - { - name: 'macproto', - type: 'keyword', - description: 'ethernet packet type ID field', - }, - { - name: 'obj', - type: 'keyword', - description: 'lspp object context string', - }, - { - name: 'ipid', - type: 'keyword', - description: 'IP datagram fragment identifier', - }, - { - name: 'new-fs', - type: 'keyword', - description: 'file system being added to vm', - }, - { - name: 'vm-pid', - type: 'keyword', - description: "vm's process ID", - }, - { - name: 'cap_pi', - type: 'keyword', - description: 'process inherited capability map', - }, - { - name: 'old-auid', - type: 'keyword', - description: 'previous auid value', - }, - { - name: 'oses', - type: 'keyword', - description: "object's session ID", - }, - { - name: 'fd', - type: 'keyword', - description: 'file descriptor number', - }, - { - name: 'igid', - type: 'keyword', - description: "ipc object's group ID", - }, - { - name: 'new-disk', - type: 'keyword', - description: 'disk being added to vm', - }, - { - name: 'parent', - type: 'keyword', - description: 'the inode number of the parent file', - }, - { - name: 'len', - type: 'keyword', - description: 'length', - }, - { - name: 'oflag', - type: 'keyword', - description: 'open syscall flags', - }, - { - name: 'uuid', - type: 'keyword', - description: 'a UUID', - }, - { - name: 'code', - type: 'keyword', - description: 'seccomp action code', - }, - { - name: 'nlnk-grp', - type: 'keyword', - description: 'netlink group number', - }, - { - name: 'cap_fp', - type: 'keyword', - description: 'file permitted capability map', - }, - { - name: 'new-mem', - type: 'keyword', - description: 'new amount of memory in KB', - }, - { - name: 'seperm', - type: 'keyword', - description: 'SELinux permission being decided on', - }, - { - name: 'enforcing', - type: 'keyword', - description: 'new MAC enforcement status', - }, - { - name: 'new-chardev', - type: 'keyword', - description: 'new character device being assigned to vm', - }, - { - name: 'old-rng', - type: 'keyword', - description: 'device name of rng being removed from a vm', - }, - { - name: 'outif', - type: 'keyword', - description: 'out interface number', - }, - { - name: 'cmd', - type: 'keyword', - description: 'command being executed', - }, - { - name: 'hook', - type: 'keyword', - description: 'netfilter hook that packet came from', - }, - { - name: 'new-level', - type: 'keyword', - description: 'new run level', - }, - { - name: 'sauid', - type: 'keyword', - description: 'sent login user ID', - }, - { - name: 'sig', - type: 'keyword', - description: 'signal number', - }, - { - name: 'audit_backlog_wait_time', - type: 'keyword', - description: "audit system's backlog wait time", - }, - { - name: 'printer', - type: 'keyword', - description: 'printer name', - }, - { - name: 'old-mem', - type: 'keyword', - description: 'present amount of memory in KB', - }, - { - name: 'perm', - type: 'keyword', - description: 'the file permission being used', - }, - { - name: 'old_pi', - type: 'keyword', - description: 'old process inherited capability map', - }, - { - name: 'state', - type: 'keyword', - description: 'audit daemon configuration resulting state', - }, - { - name: 'format', - type: 'keyword', - description: "audit log's format", - }, - { - name: 'new_gid', - type: 'keyword', - description: 'new group ID being assigned', - }, - { - name: 'tcontext', - type: 'keyword', - description: "the target's or object's context string", - }, - { - name: 'maj', - type: 'keyword', - description: 'device major number', - }, - { - name: 'watch', - type: 'keyword', - description: 'file name in a watch record', - }, - { - name: 'device', - type: 'keyword', - description: 'device name', - }, - { - name: 'grp', - type: 'keyword', - description: 'group name', - }, - { - name: 'bool', - type: 'keyword', - description: 'name of SELinux boolean', - }, - { - name: 'icmp_type', - type: 'keyword', - description: 'type of icmp message', - }, - { - name: 'new_lock', - type: 'keyword', - description: 'new value of feature lock', - }, - { - name: 'old_prom', - type: 'keyword', - description: 'network promiscuity flag', - }, - { - name: 'acl', - type: 'keyword', - description: 'access mode of resource assigned to vm', - }, - { - name: 'ip', - type: 'keyword', - description: 'network address of a printer', - }, - { - name: 'new_pi', - type: 'keyword', - description: 'new process inherited capability map', - }, - { - name: 'default-context', - type: 'keyword', - description: 'default MAC context', - }, - { - name: 'inode_gid', - type: 'keyword', - description: "group ID of the inode's owner", - }, - { - name: 'new-log_passwd', - type: 'keyword', - description: 'new value for TTY password logging', - }, - { - name: 'new_pe', - type: 'keyword', - description: 'new process effective capability map', - }, - { - name: 'selected-context', - type: 'keyword', - description: 'new MAC context assigned to session', - }, - { - name: 'cap_fver', - type: 'keyword', - description: 'file system capabilities version number', - }, - { - name: 'file', - type: 'keyword', - description: 'file name', - }, - { - name: 'net', - type: 'keyword', - description: 'network MAC address', - }, - { - name: 'virt', - type: 'keyword', - description: 'kind of virtualization being referenced', - }, - { - name: 'cap_pp', - type: 'keyword', - description: 'process permitted capability map', - }, - { - name: 'old-range', - type: 'keyword', - description: 'present SELinux range', - }, - { - name: 'resrc', - type: 'keyword', - description: 'resource being assigned', - }, - { - name: 'new-range', - type: 'keyword', - description: 'new SELinux range', - }, - { - name: 'obj_gid', - type: 'keyword', - description: 'group ID of object', - }, - { - name: 'proto', - type: 'keyword', - description: 'network protocol', - }, - { - name: 'old-disk', - type: 'keyword', - description: 'disk being removed from vm', - }, - { - name: 'audit_failure', - type: 'keyword', - description: "audit system's failure mode", - }, - { - name: 'inif', - type: 'keyword', - description: 'in interface number', - }, - { - name: 'vm', - type: 'keyword', - description: 'virtual machine name', - }, - { - name: 'flags', - type: 'keyword', - description: 'mmap syscall flags', - }, - { - name: 'nlnk-fam', - type: 'keyword', - description: 'netlink protocol number', - }, - { - name: 'old-fs', - type: 'keyword', - description: 'file system being removed from vm', - }, - { - name: 'old-ses', - type: 'keyword', - description: 'previous ses value', - }, - { - name: 'seqno', - type: 'keyword', - description: 'sequence number', - }, - { - name: 'fver', - type: 'keyword', - description: 'file system capabilities version number', - }, - { - name: 'qbytes', - type: 'keyword', - description: 'ipc objects quantity of bytes', - }, - { - name: 'seuser', - type: 'keyword', - description: "user's SE Linux user acct", - }, - { - name: 'cap_fe', - type: 'keyword', - description: 'file assigned effective capability map', - }, - { - name: 'new-vcpu', - type: 'keyword', - description: 'new number of CPU cores', - }, - { - name: 'old-level', - type: 'keyword', - description: 'old run level', - }, - { - name: 'old_pp', - type: 'keyword', - description: 'old process permitted capability map', - }, - { - name: 'daddr', - type: 'keyword', - description: 'remote IP address', - }, - { - name: 'old-role', - type: 'keyword', - description: 'present SELinux role', - }, - { - name: 'ioctlcmd', - type: 'keyword', - description: 'The request argument to the ioctl syscall', - }, - { - name: 'smac', - type: 'keyword', - description: 'local MAC address', - }, - { - name: 'apparmor', - type: 'keyword', - description: 'apparmor event information', - }, - { - name: 'fe', - type: 'keyword', - description: 'file assigned effective capability map', - }, - { - name: 'perm_mask', - type: 'keyword', - description: 'file permission mask that triggered a watch event', - }, - { - name: 'ses', - type: 'keyword', - description: 'login session ID', - }, - { - name: 'cap_fi', - type: 'keyword', - description: 'file inherited capability map', - }, - { - name: 'obj_uid', - type: 'keyword', - description: 'user ID of object', - }, - { - name: 'reason', - type: 'keyword', - description: 'text string denoting a reason for the action', - }, - { - name: 'list', - type: 'keyword', - description: "the audit system's filter list number", - }, - { - name: 'old_lock', - type: 'keyword', - description: 'present value of feature lock', - }, - { - name: 'bus', - type: 'keyword', - description: 'name of subsystem bus a vm resource belongs to', - }, - { - name: 'old_pe', - type: 'keyword', - description: 'old process effective capability map', - }, - { - name: 'new-role', - type: 'keyword', - description: 'new SELinux role', - }, - { - name: 'prom', - type: 'keyword', - description: 'network promiscuity flag', - }, - { - name: 'uri', - type: 'keyword', - description: 'URI pointing to a printer', - }, - { - name: 'audit_enabled', - type: 'keyword', - description: "audit systems's enable/disable status", - }, - { - name: 'old-log_passwd', - type: 'keyword', - description: 'present value for TTY password logging', - }, - { - name: 'old-seuser', - type: 'keyword', - description: 'present SELinux user', - }, - { - name: 'per', - type: 'keyword', - description: 'linux personality', - }, - { - name: 'scontext', - type: 'keyword', - description: "the subject's context string", - }, - { - name: 'tclass', - type: 'keyword', - description: "target's object classification", - }, - { - name: 'ver', - type: 'keyword', - description: "audit daemon's version number", - }, - { - name: 'new', - type: 'keyword', - description: 'value being set in feature', - }, - { - name: 'val', - type: 'keyword', - description: 'generic value associated with the operation', - }, - { - name: 'img-ctx', - type: 'keyword', - description: "the vm's disk image context string", - }, - { - name: 'old-chardev', - type: 'keyword', - description: 'present character device assigned to vm', - }, - { - name: 'old_val', - type: 'keyword', - description: 'current value of SELinux boolean', - }, - { - name: 'success', - type: 'keyword', - description: 'whether the syscall was successful or not', - }, - { - name: 'inode_uid', - type: 'keyword', - description: "user ID of the inode's owner", - }, - { - name: 'removed', - type: 'keyword', - description: 'number of deleted files', - }, - { - name: 'socket', - type: 'group', - fields: [ - { - name: 'port', - type: 'keyword', - description: 'The port number.', - }, - { - name: 'saddr', - type: 'keyword', - description: 'The raw socket address structure.', - }, - { - name: 'addr', - type: 'keyword', - description: 'The remote address.', - }, - { - name: 'family', - type: 'keyword', - example: 'unix', - description: 'The socket family (unix, ipv4, ipv6, netlink).', - }, - { - name: 'path', - type: 'keyword', - description: 'This is the path associated with a unix socket.', - }, - ], - }, - ], - }, - { - name: 'messages', - type: 'alias', - migration: true, - path: 'event.original', - description: - 'An ordered list of the raw messages received from the kernel that were used to construct this document. This field is present if an error occurred processing the data or if `include_raw_message` is set in the config.\n', - }, - { - name: 'warnings', - type: 'alias', - migration: true, - path: 'error.message', - description: - 'The warnings generated by the Beat during the construction of the event. These are disabled by default and are used for development and debug purposes only.\n', - }, - ], - }, - { - name: 'geoip', - type: 'group', - description: - 'The geoip fields are defined as a convenience in case you decide to enrich the data using a geoip filter in Logstash or Ingest Node.\n', - fields: [ - { - name: 'continent_name', - type: 'keyword', - description: 'The name of the continent.\n', - }, - { - name: 'city_name', - type: 'keyword', - description: 'The name of the city.\n', - }, - { - name: 'region_name', - type: 'keyword', - description: 'The name of the region.\n', - }, - { - name: 'country_iso_code', - type: 'keyword', - description: 'Country ISO code.\n', - }, - { - name: 'location', - type: 'geo_point', - description: 'The longitude and latitude.\n', - }, - ], - }, - ], - }, - { - key: 'file_integrity', - title: 'File Integrity', - description: 'These are the fields generated by the file_integrity module.', - fields: [ - { - name: 'hash', - type: 'group', - description: - 'Hashes of the file. The keys are algorithm names and the values are the hex encoded digest values.\n', - fields: [ - { - name: 'blake2b_256', - type: 'keyword', - description: 'BLAKE2b-256 hash of the file.', - }, - { - name: 'blake2b_384', - type: 'keyword', - description: 'BLAKE2b-384 hash of the file.', - }, - { - name: 'blake2b_512', - type: 'keyword', - description: 'BLAKE2b-512 hash of the file.', - }, - { - name: 'md5', - overwrite: true, - type: 'keyword', - description: 'MD5 hash of the file.', - }, - { - name: 'sha1', - overwrite: true, - type: 'keyword', - description: 'SHA1 hash of the file.', - }, - { - name: 'sha224', - type: 'keyword', - description: 'SHA224 hash of the file.', - }, - { - name: 'sha256', - overwrite: true, - type: 'keyword', - description: 'SHA256 hash of the file.', - }, - { - name: 'sha384', - type: 'keyword', - description: 'SHA384 hash of the file.', - }, - { - name: 'sha3_224', - type: 'keyword', - description: 'SHA3_224 hash of the file.', - }, - { - name: 'sha3_256', - type: 'keyword', - description: 'SHA3_256 hash of the file.', - }, - { - name: 'sha3_384', - type: 'keyword', - description: 'SHA3_384 hash of the file.', - }, - { - name: 'sha3_512', - type: 'keyword', - description: 'SHA3_512 hash of the file.', - }, - { - name: 'sha512', - overwrite: true, - type: 'keyword', - description: 'SHA512 hash of the file.', - }, - { - name: 'sha512_224', - type: 'keyword', - description: 'SHA512/224 hash of the file.', - }, - { - name: 'sha512_256', - type: 'keyword', - description: 'SHA512/256 hash of the file.', - }, - { - name: 'xxh64', - type: 'keyword', - description: 'XX64 hash of the file.', - }, - ], - }, - ], - }, - { - key: 'system', - title: 'System', - description: 'These are the fields generated by the system module.\n', - release: 'beta', - fields: [ - { - name: 'event', - type: 'group', - fields: [ - { - name: 'origin', - type: 'keyword', - description: - 'Origin of the event. This can be a file path (e.g. `/var/log/log.1`), or the name of the system component that supplied the data (e.g. `netlink`).\n', - }, - ], - }, - { - name: 'user', - type: 'group', - fields: [ - { - name: 'entity_id', - type: 'keyword', - description: - 'ID uniquely identifying the user on a host. It is computed as a SHA-256 hash of the host ID, user ID, and user name.\n', - }, - { - name: 'terminal', - type: 'keyword', - description: 'Terminal of the user.\n', - }, - ], - }, - { - name: 'process', - type: 'group', - fields: [ - { - name: 'hash', - type: 'group', - description: - 'Hashes of the executable. The keys are algorithm names and the values are the hex encoded digest values.\n', - fields: [ - { - name: 'blake2b_256', - type: 'keyword', - description: 'BLAKE2b-256 hash of the executable.', - }, - { - name: 'blake2b_384', - type: 'keyword', - description: 'BLAKE2b-384 hash of the executable.', - }, - { - name: 'blake2b_512', - type: 'keyword', - description: 'BLAKE2b-512 hash of the executable.', - }, - { - name: 'sha224', - type: 'keyword', - description: 'SHA224 hash of the executable.', - }, - { - name: 'sha384', - type: 'keyword', - description: 'SHA384 hash of the executable.', - }, - { - name: 'sha3_224', - type: 'keyword', - description: 'SHA3_224 hash of the executable.', - }, - { - name: 'sha3_256', - type: 'keyword', - description: 'SHA3_256 hash of the executable.', - }, - { - name: 'sha3_384', - type: 'keyword', - description: 'SHA3_384 hash of the executable.', - }, - { - name: 'sha3_512', - type: 'keyword', - description: 'SHA3_512 hash of the executable.', - }, - { - name: 'sha512_224', - type: 'keyword', - description: 'SHA512/224 hash of the executable.', - }, - { - name: 'sha512_256', - type: 'keyword', - description: 'SHA512/256 hash of the executable.', - }, - { - name: 'xxh64', - type: 'keyword', - description: 'XX64 hash of the executable.', - }, - ], - }, - ], - }, - { - name: 'socket', - type: 'group', - fields: [ - { - name: 'entity_id', - type: 'keyword', - description: - 'ID uniquely identifying the socket. It is computed as a SHA-256 hash of the host ID, socket inode, local IP, local port, remote IP, and remote port.\n', - }, - ], - }, - { - name: 'system.audit', - type: 'group', - description: '\n', - fields: [ - { - name: 'host', - type: 'group', - description: '`host` contains general host information.\n', - release: 'beta', - fields: [ - { - name: 'uptime', - type: 'long', - format: 'duration', - input_format: 'nanoseconds', - output_format: 'asDays', - output_precision: 1, - description: 'Uptime in nanoseconds.\n', - }, - { - name: 'boottime', - type: 'date', - description: 'Boot time.\n', - }, - { - name: 'containerized', - type: 'boolean', - description: 'Set if host is a container.\n', - }, - { - name: 'timezone.name', - type: 'keyword', - description: 'Name of the timezone of the host, e.g. BST.\n', - }, - { - name: 'timezone.offset.sec', - type: 'long', - description: 'Timezone offset in seconds.\n', - }, - { - name: 'hostname', - type: 'keyword', - description: 'Hostname.\n', - }, - { - name: 'id', - type: 'keyword', - description: 'Host ID.\n', - }, - { - name: 'architecture', - type: 'keyword', - description: 'Host architecture (e.g. x86_64).\n', - }, - { - name: 'mac', - type: 'keyword', - description: 'MAC addresses.\n', - }, - { - name: 'ip', - type: 'ip', - description: 'IP addresses.\n', - }, - { - name: 'os', - type: 'group', - description: '`os` contains information about the operating system.\n', - fields: [ - { - name: 'codename', - type: 'keyword', - description: 'OS codename, if any (e.g. stretch).\n', - }, - { - name: 'platform', - type: 'keyword', - description: 'OS platform (e.g. centos, ubuntu, windows).\n', - }, - { - name: 'name', - type: 'keyword', - description: 'OS name (e.g. Mac OS X).\n', - }, - { - name: 'family', - type: 'keyword', - description: 'OS family (e.g. redhat, debian, freebsd, windows).\n', - }, - { - name: 'version', - type: 'keyword', - description: 'OS version.\n', - }, - { - name: 'kernel', - type: 'keyword', - description: "The operating system's kernel version.\n", - }, - ], - }, - ], - }, - { - name: 'package', - type: 'group', - description: '`package` contains information about an installed or removed package.\n', - release: 'beta', - fields: [ - { - name: 'entity_id', - type: 'keyword', - description: - 'ID uniquely identifying the package. It is computed as a SHA-256 hash of the host ID, package name, and package version.\n', - }, - { - name: 'name', - type: 'keyword', - description: 'Package name.\n', - }, - { - name: 'version', - type: 'keyword', - description: 'Package version.\n', - }, - { - name: 'release', - type: 'keyword', - description: 'Package release.\n', - }, - { - name: 'arch', - type: 'keyword', - description: 'Package architecture.\n', - }, - { - name: 'license', - type: 'keyword', - description: 'Package license.\n', - }, - { - name: 'installtime', - type: 'date', - description: 'Package install time.\n', - }, - { - name: 'size', - type: 'long', - description: 'Package size.\n', - }, - { - name: 'summary', - description: 'Package summary.\n', - }, - { - name: 'url', - type: 'keyword', - description: 'Package URL.\n', - }, - ], - }, - { - name: 'user', - type: 'group', - description: '`user` contains information about the users on a system.\n', - release: 'beta', - fields: [ - { - name: 'name', - type: 'keyword', - description: 'User name.\n', - }, - { - name: 'uid', - type: 'keyword', - description: 'User ID.\n', - }, - { - name: 'gid', - type: 'keyword', - description: 'Group ID.\n', - }, - { - name: 'dir', - type: 'keyword', - description: "User's home directory.\n", - }, - { - name: 'shell', - type: 'keyword', - description: 'Program to run at login.\n', - }, - { - name: 'user_information', - type: 'keyword', - description: 'General user information. On Linux, this is the gecos field.\n', - }, - { - name: 'group', - type: 'object', - description: - "`group` contains information about any groups the user is part of (beyond the user's primary group).\n", - fields: [ - { - name: 'name', - type: 'keyword', - description: 'Group name.\n', - }, - { - name: 'gid', - type: 'integer', - description: 'Group ID.\n', - }, - ], - }, - { - name: 'password', - type: 'group', - description: - "`password` contains information about a user's password (not the password itself).\n", - fields: [ - { - name: 'type', - type: 'keyword', - description: - "A user's password type. Possible values are `shadow_password` (the password hash is in the shadow file), `password_disabled`, `no_password` (this is dangerous as anyone can log in), and `crypt_password` (when the password field in /etc/passwd seems to contain an encrypted password).\n", - }, - { - name: 'last_changed', - type: 'date', - description: "The day the user's password was last changed.\n", - }, - ], - }, - ], - }, - ], - }, - ], - }, -]; diff --git a/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/ecs.ts b/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/ecs.ts deleted file mode 100644 index a439d105d63df1..00000000000000 --- a/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/ecs.ts +++ /dev/null @@ -1,5675 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/** - * An instance of the unmodified schema exported from https://github.com/elastic/ecs - * A map of `EcsNamespace.name` `->` `EcsNamespace` - * - * - NOTE: This instance does NOT include "virtual (non-spec)" ECS fields e.g `_id`. - * - NOTE: This instance does NOT include "mappings" from ECS fields, to `ECS` - * instances e.g. `@timestamp` to `timestamp` - */ - -import { Schema } from '../type'; - -export const ecsSchema: Schema = [ - { - key: 'ecs', - title: 'ECS', - description: 'ECS Fields.', - fields: [ - { - name: '@timestamp', - level: 'core', - required: true, - type: 'date', - description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', - example: '2016-05-23T08:05:34.853Z', - }, - { - name: 'labels', - level: 'core', - type: 'object', - object_type: 'keyword', - description: - 'Custom key/value pairs.\n\nCan be used to add meta information to events. Should not contain nested objects.\nAll values are stored as keyword.\n\nExample: `docker` and `k8s` labels.', - example: '{"application": "foo-bar", "env": "production"}', - }, - { - name: 'message', - level: 'core', - type: 'text', - description: - 'For log events the message field contains the log message, optimized\nfor viewing in a log viewer.\n\nFor structured logs without an original message field, other fields can be concatenated\nto form a human-readable summary of the event.\n\nIf multiple messages exist, they can be combined into one message.', - example: 'Hello World', - }, - { - name: 'tags', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - }, - { - name: 'agent', - title: 'Agent', - group: 2, - description: - 'The agent fields contain the data about the software entity, if\nany, that collects, detects, or observes events on a host, or takes measurements\non a host.\n\nExamples include Beats. Agents may also run on observers. ECS agent.* fields\nshall be populated with details of the agent running on the host or observer\nwhere the event happened or the measurement was taken.', - footnote: - 'Examples: In the case of Beats for logs, the agent.name is filebeat.\nFor APM, it is the agent running in the app/service. The agent information does\nnot change if data is sent through queuing systems like Kafka, Redis, or processing\nsystems such as Logstash or APM Server.', - type: 'group', - fields: [ - { - name: 'build.original', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Extended build information for the agent.\n\nThis field is intended to contain any build information that a data source\nmay provide, no specific formatting is required.', - example: - 'metricbeat version 7.6.0 (amd64), libbeat 7.6.0 [6a23e8f8f30f5001ba344e4e54d8d9cb82cb107c\nbuilt 2020-02-05 23:10:10 +0000 UTC]', - default_field: false, - }, - { - name: 'ephemeral_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', - example: '8a4f500d', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', - example: 'foo', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', - example: 'filebeat', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Version of the agent.', - example: '6.0.0-rc2', - }, - ], - }, - { - name: 'as', - title: 'Autonomous System', - group: 2, - description: - 'An autonomous system (AS) is a collection of connected Internet Protocol\n(IP) routing prefixes under the control of one or more network operators on\nbehalf of a single administrative entity or domain that presents a common, clearly\ndefined routing policy to the internet.', - type: 'group', - fields: [ - { - name: 'number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - ], - }, - { - name: 'client', - title: 'Client', - group: 2, - description: - 'A client is defined as the initiator of a network connection for\nevents regarding sessions, connections, or bidirectional flow records.\n\nFor TCP events, the client is the initiator of the TCP connection that sends\nthe SYN packet(s). For other protocols, the client is generally the initiator\nor requestor in the network transaction. Some systems use the term "originator"\nto refer the client in TCP connections. The client fields describe details about\nthe system acting as the client in the network event. Client fields are usually\npopulated in conjunction with server fields. Client fields are generally not\npopulated for packet-level events.\n\nClient / server representations can add semantic context to an exchange, which\nis helpful to visualize the data in certain situations. If your context falls\nin that category, you should still ensure that source and destination are filled\nappropriately.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event client addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the client to the server.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Client domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'IP address of the client (IPv4 or IPv6).', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the client.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated IP of source based NAT sessions (e.g. internal client\nto internet).\n\nTypically connections traversing load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Translated port of source based NAT sessions (e.g. internal client\nto internet).\n\nTypically connections traversing load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the client to the server.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the client.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered client domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'cloud', - title: 'Cloud', - group: 2, - description: 'Fields related to the cloud or infrastructure the events are coming\nfrom.', - footnote: - 'Examples: If Metricbeat is running on an EC2 host and fetches data\nfrom its host, the cloud info contains the data about this machine. If Metricbeat\nruns on a remote machine outside the cloud and fetches data from a service running\nin the cloud, the field contains cloud data from the machine the service is\nrunning on.', - type: 'group', - fields: [ - { - name: 'account.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The cloud account or organization id used to identify different\nentities in a multi-tenant environment.\n\nExamples: AWS account id, Google Cloud ORG Id, or other unique identifier.', - example: 666777888999, - }, - { - name: 'availability_zone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Availability zone in which this host is running.', - example: 'us-east-1c', - }, - { - name: 'instance.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Instance ID of the host machine.', - example: 'i-1234567890abcdef0', - }, - { - name: 'instance.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Instance name of the host machine.', - }, - { - name: 'machine.type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Machine type of the host machine.', - example: 't2.medium', - }, - { - name: 'provider', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the cloud provider. Example values are aws, azure, gcp,\nor digitalocean.', - example: 'aws', - }, - { - name: 'region', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Region in which this host is running.', - example: 'us-east-1', - }, - ], - }, - { - name: 'code_signature', - title: 'Code Signature', - group: 2, - description: 'These fields contain information about binary code signatures.', - type: 'group', - fields: [ - { - name: 'exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - ], - }, - { - name: 'container', - title: 'Container', - group: 2, - description: - 'Container fields are used for meta information about the specific\ncontainer that is the source of information.\n\nThese fields help correlate data based containers from any runtime.', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique container id.', - }, - { - name: 'image.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the image the container was built on.', - }, - { - name: 'image.tag', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Container image tags.', - }, - { - name: 'labels', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: 'Image labels.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Container name.', - }, - { - name: 'runtime', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Runtime managing this container.', - example: 'docker', - }, - ], - }, - { - name: 'destination', - title: 'Destination', - group: 2, - description: - 'Destination fields describe details about the destination of a packet/event.\n\nDestination fields are usually populated in conjunction with source fields.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event destination addresses are defined ambiguously. The\nevent will sometimes list an IP, a domain or a unix socket. You should always\nstore the raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the destination to the source.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Destination domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'IP address of the destination (IPv4 or IPv6).', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the destination.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated ip of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Port the source session is translated to by NAT Device.\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the destination to the source.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the destination.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered destination domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'dll', - title: 'DLL', - group: 2, - description: - 'These fields contain information about code libraries dynamically\nloaded into processes.\n\n\nMany operating systems refer to "shared code libraries" with different names,\nbut this field set refers to all of the following:\n\n* Dynamic-link library (`.dll`) commonly used on Windows\n\n* Shared Object (`.so`) commonly used on Unix-like operating systems\n\n* Dynamic library (`.dylib`) commonly used on macOS', - type: 'group', - fields: [ - { - name: 'code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - default_field: false, - }, - { - name: 'hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - default_field: false, - }, - { - name: 'hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - default_field: false, - }, - { - name: 'hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - default_field: false, - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the library.\n\nThis generally maps to the name of the file on disk.', - example: 'kernel32.dll', - default_field: false, - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Full file path of the library.', - example: 'C:\\Windows\\System32\\kernel32.dll', - default_field: false, - }, - { - name: 'pe.architecture', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'CPU architecture target for the file.', - example: 'x64', - default_field: false, - }, - { - name: 'pe.company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'pe.description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'pe.file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'pe.imphash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash of the imports in a PE file. An imphash -- or import hash\n-- can be used to fingerprint binaries even after recompilation or other code-level\ntransformations have occurred, which would change more traditional hash values.\n\nLearn more at https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html.', - example: '0c6803c4e922103c4dca5963aad36ddf', - default_field: false, - }, - { - name: 'pe.original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'pe.product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - ], - }, - { - name: 'dns', - title: 'DNS', - group: 2, - description: - 'Fields describing DNS queries and answers.\n\nDNS events should either represent a single DNS query prior to getting answers\n(`dns.type:query`) or they should represent a full exchange and contain the\nquery details as well as all of the answers that were provided for this query\n(`dns.type:answer`).', - type: 'group', - fields: [ - { - name: 'answers', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'An array containing an object for each answer section returned\nby the server.\n\nThe main keys that should be present in these objects are defined by ECS.\nRecords that have more information may contain more keys than what ECS defines.\n\nNot all DNS data sources give all details about DNS answers. At minimum, answer\nobjects must contain the `data` key. If more information is available, map\nas much of it to ECS as possible, and add any additional fields to the answer\nobjects as custom fields.', - }, - { - name: 'answers.class', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The class of DNS data contained in this resource record.', - example: 'IN', - }, - { - name: 'answers.data', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The data describing the resource.\n\nThe meaning of this data depends on the type and class of the resource record.', - example: '10.10.10.10', - }, - { - name: 'answers.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The domain name to which this resource record pertains.\n\nIf a chain of CNAME is being resolved, each answer `name` should be the\none that corresponds with the answer `data`. It should not simply be the\noriginal `question.name` repeated.', - example: 'www.google.com', - }, - { - name: 'answers.ttl', - level: 'extended', - type: 'long', - description: - 'The time interval in seconds that this resource record may be cached\nbefore it should be discarded. Zero values mean that the data should not be\ncached.', - example: 180, - }, - { - name: 'answers.type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The type of data contained in this resource record.', - example: 'CNAME', - }, - { - name: 'header_flags', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of 2 letter DNS header flags.\n\nExpected values are: AA, TC, RD, RA, AD, CD, DO.', - example: ['RD', 'RA'], - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The DNS packet identifier assigned by the program that generated\nthe query. The identifier is copied to the response.', - example: 62111, - }, - { - name: 'op_code', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The DNS operation code that specifies the kind of query in the\nmessage. This value is set by the originator of a query and copied into the\nresponse.', - example: 'QUERY', - }, - { - name: 'question.class', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The class of records being queried.', - example: 'IN', - }, - { - name: 'question.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The name being queried.\n\nIf the name field contains non-printable characters (below 32 or above 126),\nthose characters should be represented as escaped base 10 integers (\\DDD).\nBack slashes and quotes should be escaped. Tabs, carriage returns, and line\nfeeds should be converted to \\t, \\r, and \\n respectively.', - example: 'www.google.com', - }, - { - name: 'question.registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'question.subdomain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The subdomain is all of the labels under the registered_domain.\n\nIf the domain has multiple levels of subdomain, such as "sub2.sub1.example.com",\nthe subdomain field should contain "sub2.sub1", with no trailing period.', - example: 'www', - }, - { - name: 'question.top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'question.type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The type of record being queried.', - example: 'AAAA', - }, - { - name: 'resolved_ip', - level: 'extended', - type: 'ip', - description: - 'Array containing all IPs seen in `answers.data`.\n\nThe `answers` array can be difficult to use, because of the variety of data\nformats it can contain. Extracting all IP addresses seen in there to `dns.resolved_ip`\nmakes it possible to index them as IP addresses, and makes them easier to\nvisualize and query for.', - example: ['10.10.10.10', '10.10.10.11'], - }, - { - name: 'response_code', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The DNS response code.', - example: 'NOERROR', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of DNS event captured, query or answer.\n\nIf your source of DNS events only gives you DNS queries, you should only create\ndns events of type `dns.type:query`.\n\nIf your source of DNS events gives you answers as well, you should create\none event per query (optionally as soon as the query is seen). And a second\nevent containing all query details as well as an array of answers.', - example: 'answer', - }, - ], - }, - { - name: 'ecs', - title: 'ECS', - group: 2, - description: 'Meta-information specific to ECS.', - type: 'group', - fields: [ - { - name: 'version', - level: 'core', - required: true, - type: 'keyword', - ignore_above: 1024, - description: - 'ECS version this event conforms to. `ecs.version` is a required\nfield and must exist in all events.\n\nWhen querying across multiple indices -- which may conform to slightly different\nECS versions -- this field lets integrations adjust to the schema version\nof the events.', - example: '1.0.0', - }, - ], - }, - { - name: 'error', - title: 'Error', - group: 2, - description: - 'These fields can represent errors of any kind.\n\nUse them for errors that happen while fetching events or in cases where the\nevent itself contains an error.', - type: 'group', - fields: [ - { - name: 'code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Error code describing the error.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the error.', - }, - { - name: 'message', - level: 'core', - type: 'text', - description: 'Error message.', - }, - { - name: 'stack_trace', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The stack trace of this error in plain text.', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The type of the error, for example the class name of the exception.', - example: 'java.lang.NullPointerException', - }, - ], - }, - { - name: 'event', - title: 'Event', - group: 2, - description: - 'The event fields are used for context information about the log\nor metric event itself.\n\nA log is defined as an event containing details of something that happened.\nLog events must include the time at which the thing happened. Examples of log\nevents include a process starting on a host, a network packet being sent from\na source to a destination, or a network connection between a client and a server\nbeing initiated or closed. A metric is defined as an event containing one or\nmore numerical measurements and the time at which the measurement was taken.\nExamples of metric events include memory pressure measured on a host and device\ntemperature. See the `event.kind` definition in this section for additional\ndetails about metric and state events.', - type: 'group', - fields: [ - { - name: 'action', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The action captured by the event.\n\nThis describes the information in the event. It is more specific than `event.category`.\nExamples are `group-add`, `process-started`, `file-created`. The value is\nnormally defined by the implementer.', - example: 'user-password-change', - }, - { - name: 'category', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nsecond level in the ECS category hierarchy.\n\n`event.category` represents the "big buckets" of ECS categories. For example,\nfiltering on `event.category:process` yields all events relating to process\nactivity. This field is closely related to `event.type`, which is used as\na subcategory.\n\nThis field is an array. This will allow proper categorization of some events\nthat fall in multiple categories.', - example: 'authentication', - }, - { - name: 'code', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Identification code for this event, if one exists.\n\nSome event sources use event codes to identify messages unambiguously, regardless\nof message language or wording adjustments over time. An example of this is\nthe Windows Event ID.', - example: 4648, - }, - { - name: 'created', - level: 'core', - type: 'date', - description: - 'event.created contains the date/time when the event was first\nread by an agent, or by your pipeline.\n\nThis field is distinct from @timestamp in that @timestamp typically contain\nthe time extracted from the original event.\n\nIn most situations, these two timestamps will be slightly different. The difference\ncan be used to calculate the delay between your source generating an event,\nand the time when your agent first processed it. This can be used to monitor\nyour agent or pipeline ability to keep up with your event source.\n\nIn case the two timestamps are identical, @timestamp should be used.', - example: '2016-05-23T08:05:34.857Z', - }, - { - name: 'dataset', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the dataset.\n\nIf an event source publishes more than one type of log or events (e.g. access\nlog, error log), the dataset is used to specify which one the event comes\nfrom.\n\nIt is recommended but not required to start the dataset name with the module\nname, followed by a dot, then the dataset name.', - example: 'apache.access', - }, - { - name: 'duration', - level: 'core', - type: 'long', - format: 'duration', - input_format: 'nanoseconds', - output_format: 'asMilliseconds', - output_precision: 1, - description: - 'Duration of the event in nanoseconds.\n\nIf event.start and event.end are known this value should be the difference\nbetween the end and start time.', - }, - { - name: 'end', - level: 'extended', - type: 'date', - description: - 'event.end contains the date when the event ended or when the activity\nwas last observed.', - }, - { - name: 'hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Hash (perhaps logstash fingerprint) of raw field to be able to\ndemonstrate log integrity.', - example: '123456789012345678901234567890ABCD', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique ID to describe the event.', - example: '8a4f500d', - }, - { - name: 'ingested', - level: 'core', - type: 'date', - description: - 'Timestamp when an event arrived in the central data store.\n\nThis is different from `@timestamp`, which is when the event originally occurred. It is\nalso different from `event.created`, which is meant to capture the first time\nan agent saw the event.\n\nIn normal conditions, assuming no tampering, the timestamps should chronologically\nlook like this: `@timestamp` < `event.created` < `event.ingested`.', - example: '2016-05-23T08:05:35.101Z', - default_field: false, - }, - { - name: 'kind', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nhighest level in the ECS category hierarchy.\n\n`event.kind` gives high-level information about what type of information the\nevent contains, without being specific to the contents of the event. For example,\nvalues of this field distinguish alert events from metric events.\n\nThe value of this field can be used to inform how these kinds of events should\nbe handled. They may warrant different retention, different access control,\nit may also help understand whether the data coming in at a regular interval\nor not.', - example: 'alert', - }, - { - name: 'module', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the module this data is coming from.\n\nIf your monitoring agent supports the concept of modules or plugins to process\nevents of a given source (e.g. Apache logs), `event.module` should contain\nthe name of this module.', - example: 'apache', - }, - { - name: 'original', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Raw text message of entire event. Used to demonstrate log integrity.\n\nThis field is not indexed and doc_values are disabled. It cannot be searched,\nbut it can be retrieved from `_source`.', - example: - 'Sep 19 08:26:10 host CEF:0|Security| threatmanager|1.0|100|\nworm successfully stopped|10|src=10.0.0.1 dst=2.1.2.2spt=1232', - }, - { - name: 'outcome', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nlowest level in the ECS category hierarchy.\n\n`event.outcome` simply denotes whether the event represents a success or a\nfailure from the perspective of the entity that produced the event.\n\nNote that when a single transaction is described in multiple events, each\nevent may populate different values of `event.outcome`, according to their\nperspective.\n\nAlso note that in the case of a compound event (a single event that contains\nmultiple logical events), this field should be populated with the value that\nbest captures the overall success or failure from the perspective of the event\nproducer.\n\nFurther note that not all events will have an associated outcome. For example,\nthis field is generally not populated for metric events, events with `event.type:info`,\nor any events for which an outcome does not make logical sense.', - example: 'success', - }, - { - name: 'provider', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Source of the event.\n\nEvent transports such as Syslog or the Windows Event Log typically mention\nthe source of an event. It can be the name of the software that generated\nthe event (e.g. Sysmon, httpd), or of a subsystem of the operating system\n(kernel, Microsoft-Windows-Security-Auditing).', - example: 'kernel', - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Reference URL linking to additional information about this event.\n\nThis URL links to a static definition of the this event. Alert events, indicated\nby `event.kind:alert`, are a common use case for this field.', - example: 'https://system.vendor.com/event/#0001234', - default_field: false, - }, - { - name: 'risk_score', - level: 'core', - type: 'float', - description: - "Risk score or priority of the event (e.g. security solutions).\nUse your system's original value here.", - }, - { - name: 'risk_score_norm', - level: 'extended', - type: 'float', - description: - 'Normalized risk score or priority of the event, on a scale of\n0 to 100.\n\nThis is mainly useful if you use more than one system that assigns risk scores,\nand you want to see a normalized value across all systems.', - }, - { - name: 'sequence', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Sequence number of the event.\n\nThe sequence number is a value published by some event sources, to make the\nexact ordering of events unambiguous, regardless of the timestamp precision.', - }, - { - name: 'severity', - level: 'core', - type: 'long', - format: 'string', - description: - 'The numeric severity of the event according to your event source.\n\nWhat the different severity values mean can be different between sources and\nuse cases. It is up to the implementer to make sure severities are consistent\nacross events from the same source.\n\nThe Syslog severity belongs in `log.syslog.severity.code`. `event.severity`\nis meant to represent the severity according to the event source (e.g. firewall,\nIDS). If the event source does not publish its own severity, you may optionally\ncopy the `log.syslog.severity.code` to `event.severity`.', - example: 7, - }, - { - name: 'start', - level: 'extended', - type: 'date', - description: - 'event.start contains the date when the event started or when the\nactivity was first observed.', - }, - { - name: 'timezone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'This field should be populated when the event timestamp does\nnot include timezone information already (e.g. default Syslog timestamps).\nIt is optional otherwise.\n\nAcceptable timezone formats are: a canonical ID (e.g. "Europe/Amsterdam"),\nabbreviated (e.g. "EST") or an HH:mm differential (e.g. "-05:00").', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nthird level in the ECS category hierarchy.\n\n`event.type` represents a categorization "sub-bucket" that, when used along\nwith the `event.category` field values, enables filtering events down to a\nlevel appropriate for single visualization.\n\nThis field is an array. This will allow proper categorization of some events\nthat fall in multiple event types.', - }, - { - name: 'url', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'URL linking to an external system to continue investigation of\nthis event.\n\nThis URL links to another system where in-depth investigation of the specific\noccurence of this event can take place. Alert events, indicated by `event.kind:alert`,\nare a common use case for this field.', - example: 'https://mysystem.mydomain.com/alert/5271dedb-f5b0-4218-87f0-4ac4870a38fe', - default_field: false, - }, - ], - }, - { - name: 'file', - title: 'File', - group: 2, - description: - 'A file is defined as a set of information that has been created\non, or has existed on a filesystem.\n\nFile objects can be associated with host events, network events, and/or file\nevents (e.g., those produced by File Integrity Monitoring [FIM] products or\nservices). File fields provide details about the affected file associated with\nthe event or metric.', - type: 'group', - fields: [ - { - name: 'accessed', - level: 'extended', - type: 'date', - description: - 'Last time the file was accessed.\n\nNote that not all filesystems keep track of access time.', - }, - { - name: 'attributes', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of file attributes.\n\nAttributes names will vary by platform. Here is a non-exhaustive list of values\nthat are expected in this field: archive, compressed, directory, encrypted,\nexecute, hidden, read, readonly, system, write.', - example: '["readonly", "system"]', - default_field: false, - }, - { - name: 'code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'created', - level: 'extended', - type: 'date', - description: - 'File creation time.\n\nNote that not all filesystems store the creation time.', - }, - { - name: 'ctime', - level: 'extended', - type: 'date', - description: - 'Last time the file attributes or metadata changed.\n\nNote that changes to the file content will update `mtime`. This implies `ctime`\nwill be adjusted at the same time, since `mtime` is an attribute of the file.', - }, - { - name: 'device', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Device that is the source of the file.', - example: 'sda', - }, - { - name: 'directory', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Directory where the file is located. It should include the drive\nletter, when appropriate.', - example: '/home/alice', - }, - { - name: 'drive_letter', - level: 'extended', - type: 'keyword', - ignore_above: 1, - description: - 'Drive letter where the file is located. This field is only relevant\non Windows.\n\nThe value should be uppercase, and not include the colon.', - example: 'C', - default_field: false, - }, - { - name: 'extension', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'File extension.', - example: 'png', - }, - { - name: 'gid', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Primary group ID (GID) of the file.', - example: '1001', - }, - { - name: 'group', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Primary group name of the file.', - example: 'alice', - }, - { - name: 'hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - }, - { - name: 'hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - }, - { - name: 'hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - }, - { - name: 'hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - }, - { - name: 'inode', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Inode representing the file in the filesystem.', - example: '256383', - }, - { - name: 'mime_type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'MIME type should identify the format of the file or stream of bytes\nusing https://www.iana.org/assignments/media-types/media-types.xhtml[IANA\nofficial types], where possible. When more than one type is applicable, the\nmost specific type should be used.', - default_field: false, - }, - { - name: 'mode', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Mode of the file in octal representation.', - example: '0640', - }, - { - name: 'mtime', - level: 'extended', - type: 'date', - description: 'Last time the file content was modified.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the file including the extension, without the directory.', - example: 'example.png', - }, - { - name: 'owner', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: "File owner's username.", - example: 'alice', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'Full path to the file, including the file name. It should include\nthe drive letter, when appropriate.', - example: '/home/alice/example.png', - }, - { - name: 'pe.architecture', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'CPU architecture target for the file.', - example: 'x64', - default_field: false, - }, - { - name: 'pe.company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'pe.description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'pe.file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'pe.imphash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash of the imports in a PE file. An imphash -- or import hash\n-- can be used to fingerprint binaries even after recompilation or other code-level\ntransformations have occurred, which would change more traditional hash values.\n\nLearn more at https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html.', - example: '0c6803c4e922103c4dca5963aad36ddf', - default_field: false, - }, - { - name: 'pe.original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'pe.product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - { - name: 'size', - level: 'extended', - type: 'long', - description: 'File size in bytes.\n\nOnly relevant when `file.type` is "file".', - example: 16384, - }, - { - name: 'target_path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Target path for symlinks.', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'File type (file, dir, or symlink).', - example: 'file', - }, - { - name: 'uid', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The user ID (UID) or security identifier (SID) of the file owner.', - example: '1001', - }, - ], - }, - { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an\nevent.\n\nThis geolocation information can be derived from techniques such as Geo IP,\nor be user-supplied.', - type: 'group', - fields: [ - { - name: 'city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - ], - }, - { - name: 'group', - title: 'Group', - group: 2, - description: - 'The group fields are meant to represent groups that are relevant\nto the event.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - ], - }, - { - name: 'hash', - title: 'Hash', - group: 2, - description: - 'The hash fields represent different hash algorithms and their values.\n\nField names for common hashes (e.g. MD5, SHA1) are predefined. Add fields for\nother hashes by lowercasing the hash algorithm name and using underscore separators\nas appropriate (snake case, e.g. sha3_512).', - type: 'group', - fields: [ - { - name: 'md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - }, - { - name: 'sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - }, - { - name: 'sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - }, - { - name: 'sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - }, - ], - }, - { - name: 'host', - title: 'Host', - group: 2, - description: - 'A host is defined as a general computing instance.\n\nECS host.* fields should be populated with details about the host on which the\nevent happened, or from which the measurement was taken. Host types include\nhardware, virtual machines, Docker containers, and Kubernetes nodes.', - type: 'group', - fields: [ - { - name: 'architecture', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system architecture.', - example: 'x86_64', - }, - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the domain of which the host is a member.\n\nFor example, on Windows this could be the host Active Directory domain\nor NetBIOS domain name. For Linux this could be the domain of the host\nLDAP provider.', - example: 'CONTOSO', - default_field: false, - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'hostname', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Hostname of the host.\n\nIt normally contains what the `hostname` command returns on the host machine.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique host id.\n\nAs hostname is not always unique, use values that are meaningful in your environment.\n\nExample: The current usage of `beat.name`.', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'Host ip addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Host mac addresses.', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Type of host.\n\nFor Cloud providers this can be the machine type like `t2.medium`. If vm,\nthis could be the container, for example, or other information meaningful\nin your environment.', - }, - { - name: 'uptime', - level: 'extended', - type: 'long', - description: 'Seconds the host has been up.', - example: 1325, - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'http', - title: 'HTTP', - group: 2, - description: - 'Fields related to HTTP activity. Use the `url` field set to store\nthe url of the request.', - type: 'group', - fields: [ - { - name: 'request.body.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the request body.', - example: 887, - }, - { - name: 'request.body.content', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The full HTTP request body.', - example: 'Hello world', - }, - { - name: 'request.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the request (body and headers).', - example: 1437, - }, - { - name: 'request.method', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'HTTP request method.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'get, post, put', - }, - { - name: 'request.referrer', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Referrer for this HTTP request.', - example: 'https://blog.example.com/', - }, - { - name: 'response.body.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the response body.', - example: 887, - }, - { - name: 'response.body.content', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The full HTTP response body.', - example: 'Hello world', - }, - { - name: 'response.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the response (body and headers).', - example: 1437, - }, - { - name: 'response.status_code', - level: 'extended', - type: 'long', - format: 'string', - description: 'HTTP response status code.', - example: 404, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'HTTP version.', - example: 1.1, - }, - ], - }, - { - name: 'interface', - title: 'Interface', - group: 2, - description: - 'The interface fields are used to record ingress and egress interface\ninformation when reported by an observer (e.g. firewall, router, load balancer)\nin the context of the observer handling a network connection. In the case of\na single observer interface (e.g. network sensor on a span port) only the observer.ingress\ninformation should be populated.', - type: 'group', - fields: [ - { - name: 'alias', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Interface alias as reported by the system, typically used in firewall\nimplementations for e.g. inside, outside, or dmz logical interface naming.', - example: 'outside', - default_field: false, - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface ID as reported by an observer (typically SNMP interface\nID).', - example: 10, - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface name as reported by the system.', - example: 'eth0', - default_field: false, - }, - ], - }, - { - name: 'log', - title: 'Log', - group: 2, - description: - 'Details about the event logging mechanism or logging transport.\n\nThe log.* fields are typically populated with details about the logging mechanism\nused to create and/or transport the event. For example, syslog details belong\nunder `log.syslog.*`.\n\nThe details specific to your event source are typically not logged under `log.*`,\nbut rather in `event.*` or in other ECS fields.', - type: 'group', - fields: [ - { - name: 'file.path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - "Full path to the log file this event came from, including the\nfile name. It should include the drive letter, when appropriate.\n\nIf the event wasn't read from a log file, do not populate this field.", - example: '/var/log/fun-times.log', - default_field: false, - }, - { - name: 'level', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Original log level of the log event.\n\nIf the source of the event provides a log level or textual severity, this\nis the one that goes in `log.level`. If your source does not specify one,\nyou may put your event transport severity here (e.g. Syslog severity).\n\nSome examples are `warn`, `err`, `i`, `informational`.', - example: 'error', - }, - { - name: 'logger', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The name of the logger inside an application. This is usually the\nname of the class which initialized the logger, or can be a custom name.', - example: 'org.elasticsearch.bootstrap.Bootstrap', - }, - { - name: 'origin.file.line', - level: 'extended', - type: 'integer', - description: - 'The line number of the file containing the source code which originated\nthe log event.', - example: 42, - }, - { - name: 'origin.file.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The name of the file containing the source code which originated\nthe log event.\n\nNote that this field is not meant to capture the log file. The correct field\nto capture the log file is `log.file.path`.', - example: 'Bootstrap.java', - }, - { - name: 'origin.function', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The name of the function or method which originated the log event.', - example: 'init', - }, - { - name: 'original', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is the original log message and contains the full log message\nbefore splitting it up in multiple parts.\n\nIn contrast to the `message` field which can contain an extracted part of\nthe log message, this field contains the original, full log message. It can\nhave already some modifications applied like encoding or new lines removed\nto clean up the log message.\n\nThis field is not indexed and doc_values are disabled so it cannot be queried\nbut the value can be retrieved from `_source`.', - example: 'Sep 19 08:26:10 localhost My log', - }, - { - name: 'syslog', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'The Syslog metadata of the event, if the event was transmitted\nvia Syslog. Please see RFCs 5424 or 3164.', - }, - { - name: 'syslog.facility.code', - level: 'extended', - type: 'long', - format: 'string', - description: - 'The Syslog numeric facility of the log event, if available.\n\nAccording to RFCs 5424 and 3164, this value should be an integer between 0\nand 23.', - example: 23, - }, - { - name: 'syslog.facility.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The Syslog text-based facility of the log event, if available.', - example: 'local7', - }, - { - name: 'syslog.priority', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Syslog numeric priority of the event, if available.\n\nAccording to RFCs 5424 and 3164, the priority is 8 * facility + severity.\nThis number is therefore expected to contain a value between 0 and 191.', - example: 135, - }, - { - name: 'syslog.severity.code', - level: 'extended', - type: 'long', - description: - 'The Syslog numeric severity of the log event, if available.\n\nIf the event source publishing via Syslog provides a different numeric severity\nvalue (e.g. firewall, IDS), your source numeric severity should go to `event.severity`.\nIf the event source does not specify a distinct severity, you can optionally\ncopy the Syslog severity to `event.severity`.', - example: 3, - }, - { - name: 'syslog.severity.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The Syslog numeric severity of the log event, if available.\n\nIf the event source publishing via Syslog provides a different severity value\n(e.g. firewall, IDS), your source text severity should go to `log.level`.\nIf the event source does not specify a distinct severity, you can optionally\ncopy the Syslog severity to `log.level`.', - example: 'Error', - }, - ], - }, - { - name: 'network', - title: 'Network', - group: 2, - description: - 'The network is defined as the communication path over which a host\nor network event happens.\n\nThe network.* fields should be populated with details about the network activity\nassociated with an event.', - type: 'group', - fields: [ - { - name: 'application', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A name given to an application level protocol. This can be arbitrarily\nassigned for things like microservices, but also apply to things like skype,\nicq, facebook, twitter. This would be used in situations where the vendor\nor service can be decoded such as from the source/dest IP owners, ports, or\nwire format.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'aim', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: - 'Total bytes transferred in both directions.\n\nIf `source.bytes` and `destination.bytes` are known, `network.bytes` is their\nsum.', - example: 368, - }, - { - name: 'community_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash of source and destination IPs and ports, as well as the\nprotocol used in a communication. This is a tool-agnostic standard to identify\nflows.\n\nLearn more at https://github.com/corelight/community-id-spec.', - example: '1:hO+sN4H+MG5MY/8hIrXPqc4ZQz0=', - }, - { - name: 'direction', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - "Direction of the network traffic.\nRecommended values are:\n * inbound\n * outbound\n * internal\n * external\n * unknown\n\nWhen mapping events from a host-based monitoring context, populate this field from the host's point of view.\nWhen mapping events from a network or perimeter-based monitoring context, populate this field from the point of view of your network perimeter.", - example: 'inbound', - }, - { - name: 'forwarded_ip', - level: 'core', - type: 'ip', - description: 'Host IP address when the source IP address is the proxy.', - example: '192.1.1.2', - }, - { - name: 'iana_number', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'IANA Protocol Number (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml).\nStandardized list of protocols. This aligns well with NetFlow and sFlow related\nlogs which use the IANA Protocol Number.', - example: 6, - }, - { - name: 'inner', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'Network.inner fields are added in addition to network.vlan fields\nto describe the innermost VLAN when q-in-q VLAN tagging is present. Allowed\nfields include vlan.id and vlan.name. Inner vlan fields are typically used\nwhen sending traffic with multiple 802.1q encapsulations to a network sensor\n(e.g. Zeek, Wireshark.)', - default_field: false, - }, - { - name: 'inner.vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'inner.vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name given by operators to sections of their network.', - example: 'Guest Wifi', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: - 'Total packets transferred in both directions.\n\nIf `source.packets` and `destination.packets` are known, `network.packets`\nis their sum.', - example: 24, - }, - { - name: 'protocol', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'L7 Network protocol name. ex. http, lumberjack, transport protocol.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'http', - }, - { - name: 'transport', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Same as network.iana_number, but instead using the Keyword name\nof the transport layer (udp, tcp, ipv6-icmp, etc.)\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'tcp', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'In the OSI Model this would be the Network Layer. ipv4, ipv6,\nipsec, pim, etc\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'ipv4', - }, - { - name: 'vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - ], - }, - { - name: 'observer', - title: 'Observer', - group: 2, - description: - 'An observer is defined as a special network, security, or application\ndevice used to detect, observe, or create network, security, or application-related\nevents and metrics.\n\nThis could be a custom hardware appliance or a server that has been configured\nto run special network, security, or application software. Examples include\nfirewalls, web proxies, intrusion detection/prevention systems, network monitoring\nsensors, web application firewalls, data loss prevention systems, and APM servers.\nThe observer.* fields shall be populated with details of the system, if any,\nthat detects, observes and/or creates a network, security, or application event\nor metric. Message queues and ETL components used in processing events or metrics\nare not considered observers in ECS.', - type: 'group', - fields: [ - { - name: 'egress', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'Observer.egress holds information like interface number and name,\nvlan, and zone information to classify egress traffic. Single armed monitoring\nsuch as a network sensor on a span port should only use observer.ingress\nto categorize traffic.', - default_field: false, - }, - { - name: 'egress.interface.alias', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Interface alias as reported by the system, typically used in firewall\nimplementations for e.g. inside, outside, or dmz logical interface naming.', - example: 'outside', - default_field: false, - }, - { - name: 'egress.interface.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface ID as reported by an observer (typically SNMP interface\nID).', - example: 10, - default_field: false, - }, - { - name: 'egress.interface.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface name as reported by the system.', - example: 'eth0', - default_field: false, - }, - { - name: 'egress.vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'egress.vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - { - name: 'egress.zone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Network zone of outbound traffic as reported by the observer to\ncategorize the destination area of egress traffic, e.g. Internal, External,\nDMZ, HR, Legal, etc.', - example: 'Public_Internet', - default_field: false, - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'hostname', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Hostname of the observer.', - }, - { - name: 'ingress', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'Observer.ingress holds information like interface number and name,\nvlan, and zone information to classify ingress traffic. Single armed monitoring\nsuch as a network sensor on a span port should only use observer.ingress\nto categorize traffic.', - default_field: false, - }, - { - name: 'ingress.interface.alias', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Interface alias as reported by the system, typically used in firewall\nimplementations for e.g. inside, outside, or dmz logical interface naming.', - example: 'outside', - default_field: false, - }, - { - name: 'ingress.interface.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface ID as reported by an observer (typically SNMP interface\nID).', - example: 10, - default_field: false, - }, - { - name: 'ingress.interface.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface name as reported by the system.', - example: 'eth0', - default_field: false, - }, - { - name: 'ingress.vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'ingress.vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - { - name: 'ingress.zone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Network zone of incoming traffic as reported by the observer to\ncategorize the source area of ingress traffic. e.g. internal, External, DMZ,\nHR, Legal, etc.', - example: 'DMZ', - default_field: false, - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'IP addresses of the observer.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC addresses of the observer', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Custom name of the observer.\n\nThis is a name that can be given to an observer. This can be helpful for example\nif multiple firewalls of the same model are used in an organization.\n\nIf no custom name is needed, the field can be left empty.', - example: '1_proxySG', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The product name of the observer.', - example: 's200', - }, - { - name: 'serial_number', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Observer serial number.', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of the observer the data is coming from.\n\nThere is no predefined list of observer types. Some examples are `forwarder`,\n`firewall`, `ids`, `ips`, `proxy`, `poller`, `sensor`, `APM server`.', - example: 'firewall', - }, - { - name: 'vendor', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Vendor name of the observer.', - example: 'Symantec', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Observer version.', - }, - ], - }, - { - name: 'organization', - title: 'Organization', - group: 2, - description: - 'The organization fields enrich data with information about the company\nor entity the data is associated with.\n\nThese fields help you arrange or filter data stored in an index by one or multiple\norganizations.', - type: 'group', - fields: [ - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the organization.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - }, - ], - }, - { - name: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - type: 'group', - fields: [ - { - name: 'family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - ], - }, - { - name: 'package', - title: 'Package', - group: 2, - description: - 'These fields contain information about an installed software package.\nIt contains general information about a package, such as name, version or size.\nIt also contains installation details, such as time or location.', - type: 'group', - fields: [ - { - name: 'architecture', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Package architecture.', - example: 'x86_64', - }, - { - name: 'build_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the build version of the installed\npackage.\n\nFor example use the commit SHA of a non-released package.', - example: '36f4f7e89dd61b0988b12ee000b98966867710cd', - default_field: false, - }, - { - name: 'checksum', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Checksum of the installed package for verification.', - example: '68b329da9893e34099c7d8ad5cb9c940', - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Description of the package.', - example: - 'Open source programming language to build simple/reliable/efficient\nsoftware.', - }, - { - name: 'install_scope', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Indicating how the package was installed, e.g. user-local, global.', - example: 'global', - }, - { - name: 'installed', - level: 'extended', - type: 'date', - description: 'Time when package was installed.', - }, - { - name: 'license', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'License under which the package was released.\n\nUse a short name, e.g. the license identifier from SPDX License List where\npossible (https://spdx.org/licenses/).', - example: 'Apache License 2.0', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Package name', - example: 'go', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Path where the package is installed.', - example: '/usr/local/Cellar/go/1.12.9/', - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Home page or reference URL of the software in this package, if\navailable.', - example: 'https://golang.org', - default_field: false, - }, - { - name: 'size', - level: 'extended', - type: 'long', - format: 'string', - description: 'Package size in bytes.', - example: 62231, - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Type of package.\n\nThis should contain the package file type, rather than the package manager\nname. Examples: rpm, dpkg, brew, npm, gem, nupkg, jar.', - example: 'rpm', - default_field: false, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Package version', - example: '1.12.9', - }, - ], - }, - { - name: 'pe', - title: 'PE Header', - group: 2, - description: 'These fields contain Windows Portable Executable (PE) metadata.', - type: 'group', - fields: [ - { - name: 'architecture', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'CPU architecture target for the file.', - example: 'x64', - default_field: false, - }, - { - name: 'company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'imphash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash of the imports in a PE file. An imphash -- or import hash\n-- can be used to fingerprint binaries even after recompilation or other code-level\ntransformations have occurred, which would change more traditional hash values.\n\nLearn more at https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html.', - example: '0c6803c4e922103c4dca5963aad36ddf', - default_field: false, - }, - { - name: 'original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - ], - }, - { - name: 'process', - title: 'Process', - group: 2, - description: - 'These fields contain information about a process.\n\nThese fields can help you correlate metrics information with a process id/name\nfrom a log message. The `process.pid` often stays in the metric itself and\nis copied to the global field for correlation.', - type: 'group', - fields: [ - { - name: 'args', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.', - example: ['/usr/bin/ssh', '-l', 'user', '10.0.0.16'], - }, - { - name: 'args_count', - level: 'extended', - type: 'long', - description: - 'Length of the process.args array.\n\nThis field can be useful for querying or performing bucket analysis on how\nmany arguments were provided to start a process. More arguments may be an\nindication of suspicious activity.', - example: 4, - default_field: false, - }, - { - name: 'code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'command_line', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'Full command line that started the process, including the absolute\npath to the executable, and all arguments.\n\nSome arguments may be filtered to protect sensitive information.', - example: '/usr/bin/ssh -l user 10.0.0.16', - default_field: false, - }, - { - name: 'entity_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier for the process.\n\nThe implementation of this is specified by the data source, but some examples\nof what could be used here are a process-generated UUID, Sysmon Process GUIDs,\nor a hash of some uniquely identifying components of a process.\n\nConstructing a globally unique identifier is a common practice to mitigate\nPID reuse as well as to identify a specific process over time, across multiple\nmonitored hosts.', - example: 'c2c455d9f99375d', - default_field: false, - }, - { - name: 'executable', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', - }, - { - name: 'exit_code', - level: 'extended', - type: 'long', - description: - 'The exit code of the process, if this is a termination event.\n\nThe field should be absent if there is no exit code for the event (e.g. process\nstart).', - example: 137, - default_field: false, - }, - { - name: 'hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - }, - { - name: 'hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - }, - { - name: 'hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - }, - { - name: 'hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Process name.\n\nSometimes called program name or similar.', - example: 'ssh', - }, - { - name: 'parent.args', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of process arguments.\n\nMay be filtered to protect sensitive information.', - example: ['ssh', '-l', 'user', '10.0.0.16'], - default_field: false, - }, - { - name: 'parent.args_count', - level: 'extended', - type: 'long', - description: - 'Length of the process.args array.\n\nThis field can be useful for querying or performing bucket analysis on how\nmany arguments were provided to start a process. More arguments may be an\nindication of suspicious activity.', - example: 4, - default_field: false, - }, - { - name: 'parent.code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'parent.code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'parent.code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'parent.code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'parent.code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'parent.command_line', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'Full command line that started the process, including the absolute\npath to the executable, and all arguments.\n\nSome arguments may be filtered to protect sensitive information.', - example: '/usr/bin/ssh -l user 10.0.0.16', - default_field: false, - }, - { - name: 'parent.entity_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier for the process.\n\nThe implementation of this is specified by the data source, but some examples\nof what could be used here are a process-generated UUID, Sysmon Process GUIDs,\nor a hash of some uniquely identifying components of a process.\n\nConstructing a globally unique identifier is a common practice to mitigate\nPID reuse as well as to identify a specific process over time, across multiple\nmonitored hosts.', - example: 'c2c455d9f99375d', - default_field: false, - }, - { - name: 'parent.executable', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', - default_field: false, - }, - { - name: 'parent.exit_code', - level: 'extended', - type: 'long', - description: - 'The exit code of the process, if this is a termination event.\n\nThe field should be absent if there is no exit code for the event (e.g. process\nstart).', - example: 137, - default_field: false, - }, - { - name: 'parent.hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - default_field: false, - }, - { - name: 'parent.hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - default_field: false, - }, - { - name: 'parent.hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - default_field: false, - }, - { - name: 'parent.hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - default_field: false, - }, - { - name: 'parent.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'Process name.\n\nSometimes called program name or similar.', - example: 'ssh', - default_field: false, - }, - { - name: 'parent.pgid', - level: 'extended', - type: 'long', - format: 'string', - description: 'Identifier of the group of processes the process belongs to.', - default_field: false, - }, - { - name: 'parent.pid', - level: 'core', - type: 'long', - format: 'string', - description: 'Process id.', - example: 4242, - default_field: false, - }, - { - name: 'parent.ppid', - level: 'extended', - type: 'long', - format: 'string', - description: "Parent process' pid.", - example: 4241, - default_field: false, - }, - { - name: 'parent.start', - level: 'extended', - type: 'date', - description: 'The time the process started.', - example: '2016-05-23T08:05:34.853Z', - default_field: false, - }, - { - name: 'parent.thread.id', - level: 'extended', - type: 'long', - format: 'string', - description: 'Thread ID.', - example: 4242, - default_field: false, - }, - { - name: 'parent.thread.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Thread name.', - example: 'thread-0', - default_field: false, - }, - { - name: 'parent.title', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'Process title.\n\nThe proctitle, some times the same as process name. Can also be different:\nfor example a browser setting its title to the web page currently opened.', - default_field: false, - }, - { - name: 'parent.uptime', - level: 'extended', - type: 'long', - description: 'Seconds the process has been up.', - example: 1325, - default_field: false, - }, - { - name: 'parent.working_directory', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'The working directory of the process.', - example: '/home/alice', - default_field: false, - }, - { - name: 'pe.architecture', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'CPU architecture target for the file.', - example: 'x64', - default_field: false, - }, - { - name: 'pe.company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'pe.description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'pe.file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'pe.imphash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash of the imports in a PE file. An imphash -- or import hash\n-- can be used to fingerprint binaries even after recompilation or other code-level\ntransformations have occurred, which would change more traditional hash values.\n\nLearn more at https://www.fireeye.com/blog/threat-research/2014/01/tracking-malware-import-hashing.html.', - example: '0c6803c4e922103c4dca5963aad36ddf', - default_field: false, - }, - { - name: 'pe.original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'pe.product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - { - name: 'pgid', - level: 'extended', - type: 'long', - format: 'string', - description: 'Identifier of the group of processes the process belongs to.', - }, - { - name: 'pid', - level: 'core', - type: 'long', - format: 'string', - description: 'Process id.', - example: 4242, - }, - { - name: 'ppid', - level: 'extended', - type: 'long', - format: 'string', - description: "Parent process' pid.", - example: 4241, - }, - { - name: 'start', - level: 'extended', - type: 'date', - description: 'The time the process started.', - example: '2016-05-23T08:05:34.853Z', - }, - { - name: 'thread.id', - level: 'extended', - type: 'long', - format: 'string', - description: 'Thread ID.', - example: 4242, - }, - { - name: 'thread.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Thread name.', - example: 'thread-0', - }, - { - name: 'title', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'Process title.\n\nThe proctitle, some times the same as process name. Can also be different:\nfor example a browser setting its title to the web page currently opened.', - }, - { - name: 'uptime', - level: 'extended', - type: 'long', - description: 'Seconds the process has been up.', - example: 1325, - }, - { - name: 'working_directory', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The working directory of the process.', - example: '/home/alice', - }, - ], - }, - { - name: 'registry', - title: 'Registry', - group: 2, - description: 'Fields related to Windows Registry operations.', - type: 'group', - fields: [ - { - name: 'data.bytes', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Original bytes written with base64 encoding.\n\nFor Windows registry operations, such as SetValueEx and RegQueryValueEx, this\ncorresponds to the data pointed by `lp_data`. This is optional but provides\nbetter recoverability and should be populated for REG_BINARY encoded values.', - example: 'ZQBuAC0AVQBTAAAAZQBuAAAAAAA=', - default_field: false, - }, - { - name: 'data.strings', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Content when writing string types.\n\nPopulated as an array when writing string data to the registry. For single\nstring registry types (REG_SZ, REG_EXPAND_SZ), this should be an array with\none string. For sequences of string with REG_MULTI_SZ, this array will be\nvariable length. For numeric data, such as REG_DWORD and REG_QWORD, this should\nbe populated with the decimal representation (e.g `"1"`).', - example: '["C:\\rta\\red_ttp\\bin\\myapp.exe"]', - default_field: false, - }, - { - name: 'data.type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Standard registry type for encoding contents', - example: 'REG_SZ', - default_field: false, - }, - { - name: 'hive', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Abbreviated name for the hive.', - example: 'HKLM', - default_field: false, - }, - { - name: 'key', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Hive-relative path of keys.', - example: - 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\winword.exe', - default_field: false, - }, - { - name: 'path', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Full path, including hive, key and value', - example: - 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution\nOptions\\winword.exe\\Debugger', - default_field: false, - }, - { - name: 'value', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the value written.', - example: 'Debugger', - default_field: false, - }, - ], - }, - { - name: 'related', - title: 'Related', - group: 2, - description: - 'This field set is meant to facilitate pivoting around a piece of\ndata.\n\nSome pieces of information can be seen in many places in an ECS event. To facilitate\nsearching for them, store an array of all seen values to their corresponding\nfield in `related.`.\n\nA concrete example is IP addresses, which can be under host, observer, source,\ndestination, client, server, and network.forwarded_ip. If you append all IPs\nto `related.ip`, you can then search for a given IP trivially, no matter where\nit appeared, by querying `related.ip:192.0.2.15`.', - type: 'group', - fields: [ - { - name: 'hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - "All the hashes seen on your event. Populating this field, then\nusing it to search for hashes can help in situations where you're unsure what\nthe hash algorithm is (and therefore which key name to search).", - default_field: false, - }, - { - name: 'ip', - level: 'extended', - type: 'ip', - description: 'All of the IPs seen on your event.', - }, - { - name: 'user', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'All the user names seen on your event.', - default_field: false, - }, - ], - }, - { - name: 'rule', - title: 'Rule', - group: 2, - description: - 'Rule fields are used to capture the specifics of any observer or\nagent rules that generate alerts or other notable events.\n\nExamples of data sources that would populate the rule fields include: network\nadmission control platforms, network or host IDS/IPS, network firewalls, web\napplication firewalls, url filters, endpoint detection and response (EDR) systems,\netc.', - type: 'group', - fields: [ - { - name: 'author', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name, organization, or pseudonym of the author or authors who created\nthe rule used to generate this event.', - example: ['Star-Lord'], - default_field: false, - }, - { - name: 'category', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A categorization value keyword used by the entity using the rule\nfor detection of this event.', - example: 'Attempted Information Leak', - default_field: false, - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The description of the rule generating the event.', - example: 'Block requests to public DNS over HTTPS / TLS protocols', - default_field: false, - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A rule ID that is unique within the scope of an agent, observer,\nor other entity using the rule for detection of this event.', - example: 101, - default_field: false, - }, - { - name: 'license', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the license under which the rule used to generate this\nevent is made available.', - example: 'Apache 2.0', - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The name of the rule or signature generating the event.', - example: 'BLOCK_DNS_over_TLS', - default_field: false, - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Reference URL to additional information about the rule used to\ngenerate this event.\n\nThe URL can point to the vendor documentation about the rule. If that is\nnot available, it can also be a link to a more general page describing this\ntype of alert.', - example: 'https://en.wikipedia.org/wiki/DNS_over_TLS', - default_field: false, - }, - { - name: 'ruleset', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the ruleset, policy, group, or parent category in which\nthe rule used to generate this event is a member.', - example: 'Standard_Protocol_Filters', - default_field: false, - }, - { - name: 'uuid', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A rule ID that is unique within the scope of a set or group of\nagents, observers, or other entities using the rule for detection of this\nevent.', - example: 1100110011, - default_field: false, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The version / revision of the rule being used for analysis.', - example: 1.1, - default_field: false, - }, - ], - }, - { - name: 'search', - title: 'Search', - group: 2, - description: - 'The Search fields describe information about a search request event:\nquery or pagination. The fields that should be used with this field set include:\n`event.action` to describe the search action (e.g. `search.query`, `search.page`,\netc.), `event.duration` to describe the duration of a search request, `@timestamp`\nto record the event original timestamp and optionally the `source` fields\nto record context information such as `user.id` or `geo`.', - type: 'group', - fields: [ - { - name: 'query.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'An opaque query identifier. This identifier needs to be unique\nto a user query, and all subsequent events (pagination, clicks) need to have\nthe same query identifier.', - example: '2dc15175-de0d-44db-86d8-8a99f41b7a11', - default_field: false, - }, - { - name: 'query.page', - level: 'extended', - type: 'long', - description: - 'For search results that support pagination, this represents the\ncurrent page being requested. Initial search requests are `1` while subsequent\npage requests are incremental.', - example: 1, - default_field: false, - }, - { - name: 'query.value', - level: 'extended', - type: 'keyword', - ignore_above: 4096, - description: - 'The query string being searched on. This field is not analyzed\nand should not be pre-processed in any way in the event (e.g. normalization\nlist lowercasing). This is useful for search use-cases that use a one- box\nstyle search interface. Other interfaces will have to rely on additional custom\nfields or labels to represent things like filters applied, extra parameters,\nuser context, etc.', - example: 'where does the rain in Spain mainly fall', - default_field: false, - }, - { - name: 'results.ids', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - "A list of opaque document IDs representing the results that were\nshown to the user. This is effectively the impression list and it's size should\nbe equal to `results.size`. This field can be empty when there are no results\nto return.", - example: ['user:82375akja9f', 'issue:2782630'], - default_field: false, - }, - { - name: 'results.size', - level: 'extended', - type: 'long', - description: - 'The size of the result set displayed to the user. This should be\nequivalent to the length of the results in `results.ids`. This is also known\nas the page size or limit.', - example: 10, - default_field: false, - }, - { - name: 'results.total', - level: 'extended', - type: 'long', - description: - 'The total number of matches for this query. This number is always\ngreater than or equal to `results.size`. This is the `hits.total` field in\nthe query response.', - example: 134509, - default_field: false, - }, - ], - }, - { - name: 'server', - title: 'Server', - group: 2, - description: - 'A Server is defined as the responder in a network connection for\nevents regarding sessions, connections, or bidirectional flow records.\n\nFor TCP events, the server is the receiver of the initial SYN packet(s) of the\nTCP connection. For other protocols, the server is generally the responder in\nthe network transaction. Some systems actually use the term "responder" to refer\nthe server in TCP connections. The server fields describe details about the\nsystem acting as the server in the network event. Server fields are usually\npopulated in conjunction with client fields. Server fields are generally not\npopulated for packet-level events.\n\nClient / server representations can add semantic context to an exchange, which\nis helpful to visualize the data in certain situations. If your context falls\nin that category, you should still ensure that source and destination are filled\nappropriately.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event server addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the server to the client.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Server domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'IP address of the server (IPv4 or IPv6).', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the server.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated ip of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Translated port of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the server to the client.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the server.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered server domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'service', - title: 'Service', - group: 2, - description: - 'The service fields describe the service for or from which the data\nwas collected.\n\nThese fields help you find and correlate logs for a specific service and version.', - type: 'group', - fields: [ - { - name: 'ephemeral_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Ephemeral identifier of this service (if one exists).\n\nThis id normally changes across restarts, but `service.id` does not.', - example: '8a4f500f', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of the running service. If the service is comprised\nof many nodes, the `service.id` should be the same for all nodes.\n\nThis id should uniquely identify the service. This makes it possible to correlate\nlogs and metrics for one specific service, no matter which particular node\nemitted the event.\n\nNote that if you need to see the events from one specific host of the service,\nyou should filter on that `host.name` or `host.id` instead.', - example: 'd37e5ebfe0ae6c4972dbe9f0174a1637bb8247f6', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the service data is collected from.\n\nThe name of the service is normally user given. This allows for distributed\nservices that run on multiple hosts to correlate the related instances based\non the name.\n\nIn the case of Elasticsearch the `service.name` could contain the cluster\nname. For Beats the `service.name` is by default a copy of the `service.type`\nfield if no name is specified.', - example: 'elasticsearch-metrics', - }, - { - name: 'node.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of a service node.\n\nThis allows for two nodes of the same service running on the same host to\nbe differentiated. Therefore, `service.node.name` should typically be unique\nacross nodes of a given service.\n\nIn the case of Elasticsearch, the `service.node.name` could contain the unique\nnode name within the Elasticsearch cluster. In cases where the service does not\nhave the concept of a node name, the host name or container name can be used\nto distinguish running instances that make up this service. If those do not\nprovide uniqueness (e.g. multiple instances of the service running on the\nsame host) - the node name can be manually set.', - example: 'instance-0000000016', - }, - { - name: 'state', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Current state of the service.', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of the service data is collected from.\n\nThe type can be used to group and correlate logs and metrics from one service\ntype.\n\nExample: If logs or metrics are collected from Elasticsearch, `service.type`\nwould be `elasticsearch`.', - example: 'elasticsearch', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Version of the service the data was collected from.\n\nThis allows to look at a data set only for a specific version of a service.', - example: '3.2.4', - }, - ], - }, - { - name: 'source', - title: 'Source', - group: 2, - description: - 'Source fields describe details about the source of a packet/event.\n\nSource fields are usually populated in conjunction with destination fields.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event source addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the source to the destination.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Source domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'IP address of the source (IPv4 or IPv6).', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the source.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated ip of source based NAT sessions (e.g. internal client\nto internet)\n\nTypically connections traversing load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Translated port of source based NAT sessions. (e.g. internal client\nto internet)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the source to the destination.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the source.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered source domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'threat', - title: 'Threat', - group: 2, - description: - 'Fields to classify events and alerts according to a threat taxonomy\nsuch as the Mitre ATT&CK framework.\n\nThese fields are for users to classify alerts from all of their sources (e.g.\nIDS, NGFW, etc.) within a common taxonomy. The threat.tactic.* are meant to\ncapture the high level category of the threat (e.g. "impact"). The threat.technique.*\nfields are meant to capture which kind of approach is used by this detected\nthreat, to accomplish the goal (e.g. "endpoint denial of service").', - type: 'group', - fields: [ - { - name: 'framework', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the threat framework used to further categorize and classify\nthe tactic and technique of the reported threat. Framework classification\ncan be provided by detecting systems, evaluated at ingest time, or retrospectively\ntagged to events.', - example: 'MITRE ATT&CK', - }, - { - name: 'tactic.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The id of tactic used by this threat. You can use the Mitre ATT&CK\nMatrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/\n)', - example: 'TA0040', - }, - { - name: 'tactic.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the type of tactic used by this threat. You can use the\nMitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/\n)', - example: 'impact', - }, - { - name: 'tactic.reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The reference url of tactic used by this threat. You can use the\nMitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/\n)', - example: 'https://attack.mitre.org/tactics/TA0040/', - }, - { - name: 'technique.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The id of technique used by this tactic. You can use the Mitre\nATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/\n)', - example: 'T1499', - }, - { - name: 'technique.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'The name of technique used by this tactic. You can use the Mitre\nATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/\n)', - example: 'endpoint denial of service', - }, - { - name: 'technique.reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The reference url of technique used by this tactic. You can use\nthe Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/\n)', - example: 'https://attack.mitre.org/techniques/T1499/', - }, - ], - }, - { - name: 'tls', - title: 'TLS', - group: 2, - description: - 'Fields related to a TLS connection. These fields focus on the TLS\nprotocol itself and intentionally avoids in-depth analysis of the related x.509\ncertificate files.', - type: 'group', - fields: [ - { - name: 'cipher', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'String indicating the cipher used during the current connection.', - example: 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256', - default_field: false, - }, - { - name: 'client.certificate', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'PEM-encoded stand-alone certificate offered by the client. This\nis usually mutually-exclusive of `client.certificate_chain` since this value\nalso exists in that list.', - example: 'MII...', - default_field: false, - }, - { - name: 'client.certificate_chain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of PEM-encoded certificates that make up the certificate\nchain offered by the client. This is usually mutually-exclusive of `client.certificate`\nsince that value should be the first certificate in the chain.', - example: ['MII...', 'MII...'], - default_field: false, - }, - { - name: 'client.hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the MD5 digest of DER-encoded version\nof certificate offered by the client. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC', - default_field: false, - }, - { - name: 'client.hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA1 digest of DER-encoded version\nof certificate offered by the client. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '9E393D93138888D288266C2D915214D1D1CCEB2A', - default_field: false, - }, - { - name: 'client.hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA256 digest of DER-encoded\nversion of certificate offered by the client. For consistency with other hash\nvalues, this value should be formatted as an uppercase hash.', - example: '0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0', - default_field: false, - }, - { - name: 'client.issuer', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Distinguished name of subject of the issuer of the x.509 certificate\npresented by the client.', - example: 'CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'client.ja3', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash that identifies clients based on how they perform an SSL/TLS\nhandshake.', - example: 'd4e5b18d6b55c71272893221c96ba240', - default_field: false, - }, - { - name: 'client.not_after', - level: 'extended', - type: 'date', - description: - 'Date/Time indicating when client certificate is no longer considered\nvalid.', - example: '2021-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'client.not_before', - level: 'extended', - type: 'date', - description: 'Date/Time indicating when client certificate is first considered\nvalid.', - example: '1970-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'client.server_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Also called an SNI, this tells the server which hostname to which\nthe client is attempting to connect. When this value is available, it should\nget copied to `destination.domain`.', - example: 'www.elastic.co', - default_field: false, - }, - { - name: 'client.subject', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Distinguished name of subject of the x.509 certificate presented\nby the client.', - example: 'CN=myclient, OU=Documentation Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'client.supported_ciphers', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Array of ciphers offered by the client during the client hello.', - example: [ - 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384', - 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384', - '...', - ], - default_field: false, - }, - { - name: 'curve', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'String indicating the curve used for the given cipher, when applicable.', - example: 'secp256r1', - default_field: false, - }, - { - name: 'established', - level: 'extended', - type: 'boolean', - description: - 'Boolean flag indicating if the TLS negotiation was successful and\ntransitioned to an encrypted tunnel.', - default_field: false, - }, - { - name: 'next_protocol', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'String indicating the protocol being tunneled. Per the values in\nthe IANA registry (https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids),\nthis string should be lower case.', - example: 'http/1.1', - default_field: false, - }, - { - name: 'resumed', - level: 'extended', - type: 'boolean', - description: - 'Boolean flag indicating if this TLS connection was resumed from\nan existing TLS negotiation.', - default_field: false, - }, - { - name: 'server.certificate', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'PEM-encoded stand-alone certificate offered by the server. This\nis usually mutually-exclusive of `server.certificate_chain` since this value\nalso exists in that list.', - example: 'MII...', - default_field: false, - }, - { - name: 'server.certificate_chain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of PEM-encoded certificates that make up the certificate\nchain offered by the server. This is usually mutually-exclusive of `server.certificate`\nsince that value should be the first certificate in the chain.', - example: ['MII...', 'MII...'], - default_field: false, - }, - { - name: 'server.hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the MD5 digest of DER-encoded version\nof certificate offered by the server. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC', - default_field: false, - }, - { - name: 'server.hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA1 digest of DER-encoded version\nof certificate offered by the server. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '9E393D93138888D288266C2D915214D1D1CCEB2A', - default_field: false, - }, - { - name: 'server.hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA256 digest of DER-encoded\nversion of certificate offered by the server. For consistency with other hash\nvalues, this value should be formatted as an uppercase hash.', - example: '0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0', - default_field: false, - }, - { - name: 'server.issuer', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Subject of the issuer of the x.509 certificate presented by the\nserver.', - example: 'CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'server.ja3s', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash that identifies servers based on how they perform an SSL/TLS\nhandshake.', - example: '394441ab65754e2207b1e1b457b3641d', - default_field: false, - }, - { - name: 'server.not_after', - level: 'extended', - type: 'date', - description: - 'Timestamp indicating when server certificate is no longer considered\nvalid.', - example: '2021-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'server.not_before', - level: 'extended', - type: 'date', - description: 'Timestamp indicating when server certificate is first considered\nvalid.', - example: '1970-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'server.subject', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Subject of the x.509 certificate presented by the server.', - example: 'CN=www.mydomain.com, OU=Infrastructure Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Numeric part of the version parsed from the original string.', - example: '1.2', - default_field: false, - }, - { - name: 'version_protocol', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Normalized lowercase protocol name parsed from original string.', - example: 'tls', - default_field: false, - }, - ], - }, - { - name: 'tracing', - title: 'Tracing', - group: 2, - description: - 'Distributed tracing makes it possible to analyze performance throughout\na microservice architecture all in one view. This is accomplished by tracing\nall of the requests - from the initial web request in the front-end service\n- to queries made through multiple back-end services.', - type: 'group', - fields: [ - { - name: 'trace.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of the trace.\n\nA trace groups multiple events like transactions that belong together. For\nexample, a user request handled by multiple inter-connected services.', - example: '4bf92f3577b34da6a3ce929d0e0e4736', - }, - { - name: 'transaction.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of the transaction.\n\nA transaction is the highest level of work measured within a service, such\nas a request to a server.', - example: '00f067aa0ba902b7', - }, - ], - }, - { - name: 'url', - title: 'URL', - group: 2, - description: - 'URL fields provide support for complete or partial URLs, and supports\nthe breaking down into scheme, domain, path, and so on.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Domain of the url, such as "www.elastic.co".\n\nIn some cases a URL may refer to an IP and/or port directly, without a domain\nname. In this case, the IP address would go to the `domain` field.', - example: 'www.elastic.co', - }, - { - name: 'extension', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The field contains the file extension from the original request\nurl.\n\nThe file extension is only set if it exists, as not every url has a file extension.\n\nThe leading period must not be included. For example, the value must be "png",\nnot ".png".', - example: 'png', - }, - { - name: 'fragment', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Portion of the url after the `#`, such as "top".\n\nThe `#` is not part of the fragment.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'If full URLs are important to your use case, they should be stored\nin `url.full`, whether this field is reconstructed or present in the event\nsource.', - example: 'https://www.elastic.co:443/search?q=elasticsearch#top', - }, - { - name: 'original', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'Unmodified original url as seen in the event source.\n\nNote that in network monitoring, the observed URL may be a full URL, whereas\nin access logs, the URL is often just represented as a path.\n\nThis field is meant to represent the URL as it was observed, complete or not.', - example: - 'https://www.elastic.co:443/search?q=elasticsearch#top or /search?q=elasticsearch', - }, - { - name: 'password', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Password of the request.', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Path of the request, such as "/search".', - }, - { - name: 'port', - level: 'extended', - type: 'long', - format: 'string', - description: 'Port of the request, such as 443.', - example: 443, - }, - { - name: 'query', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The query field describes the query string of the request, such\nas "q=elasticsearch".\n\nThe `?` is excluded from the query string. If a URL contains no `?`, there\nis no query field. If there is a `?` but no query, the query field exists\nwith an empty string. The `exists` query can be used to differentiate between\nthe two cases.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered url domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'scheme', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Scheme of the request, such as "https".\n\nNote: The `:` is not part of the scheme.', - example: 'https', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'username', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Username of the request.', - }, - ], - }, - { - name: 'user', - title: 'User', - group: 2, - description: - 'The user fields describe information about the user that is relevant\nto the event.\n\nFields can have one entry or multiple entries. If a user has more than one id,\nprovide an array that includes all of them.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier of the user.', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'user_agent', - title: 'User agent', - group: 2, - description: - 'The user_agent fields normally come from a browser request.\n\nThey often show up in web service logs coming from the parsed user agent string.', - type: 'group', - fields: [ - { - name: 'device.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the device.', - example: 'iPhone', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the user agent.', - example: 'Safari', - }, - { - name: 'original', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'Unparsed user_agent string.', - example: - 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15\n(KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Version of the user agent.', - example: 12, - }, - ], - }, - { - name: 'vlan', - title: 'VLAN', - group: 2, - description: - 'The VLAN fields are used to identify 802.1q tag(s) of a packet,\nas well as ingress and egress VLAN associations of an observer in relation to\na specific packet or connection.\n\nNetwork.vlan fields are used to record a single VLAN tag, or the outer tag in\nthe case of q-in-q encapsulations, for a packet or connection as observed, typically\nprovided by a network sensor (e.g. Zeek, Wireshark) passively reporting on traffic.\n\nNetwork.inner VLAN fields are used to report inner q-in-q 802.1q tags (multiple\n802.1q encapsulations) as observed, typically provided by a network sensor (e.g.\nZeek, Wireshark) passively reporting on traffic. Network.inner VLAN fields should\nonly be used in addition to network.vlan fields to indicate q-in-q tagging.\n\nObserver.ingress and observer.egress VLAN values are used to record observer\nspecific information when observer events contain discrete ingress and egress\nVLAN information, typically provided by firewalls, routers, or load balancers.', - type: 'group', - fields: [ - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - ], - }, - { - name: 'vulnerability', - title: 'Vulnerability', - group: 2, - description: - 'The vulnerability fields describe information about a vulnerability\nthat is relevant to an event.', - type: 'group', - fields: [ - { - name: 'category', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of system or architecture that the vulnerability affects.\nThese may be platform-specific (for example, Debian or SUSE) or general (for\nexample, Database or Firewall). For example (https://qualysguard.qualys.com/qwebhelp/fo_portal/knowledgebase/vulnerability_categories.htm[Qualys\nvulnerability categories])\n\nThis field must be an array.', - example: '["Firewall"]', - default_field: false, - }, - { - name: 'classification', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The classification of the vulnerability scoring system. For example\n(https://www.first.org/cvss/)', - example: 'CVSS', - default_field: false, - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'The description of the vulnerability that provides additional context\nof the vulnerability. For example (https://cve.mitre.org/about/faqs.html#cve_entry_descriptions_created[Common\nVulnerabilities and Exposure CVE description])', - example: 'In macOS before 2.12.6, there is a vulnerability in the RPC...', - default_field: false, - }, - { - name: 'enumeration', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of identifier used for this vulnerability. For example\n(https://cve.mitre.org/about/)', - example: 'CVE', - default_field: false, - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The identification (ID) is the number portion of a vulnerability\nentry. It includes a unique identification number for the vulnerability. For\nexample (https://cve.mitre.org/about/faqs.html#what_is_cve_id)[Common Vulnerabilities\nand Exposure CVE ID]', - example: 'CVE-2019-00001', - default_field: false, - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A resource that provides additional information, context, and mitigations\nfor the identified vulnerability.', - example: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6111', - default_field: false, - }, - { - name: 'report_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The report or scan identification number.', - example: 20191018.0001, - default_field: false, - }, - { - name: 'scanner.vendor', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The name of the vulnerability scanner vendor.', - example: 'Tenable', - default_field: false, - }, - { - name: 'score.base', - level: 'extended', - type: 'float', - description: - 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe.\n\nBase scores cover an assessment for exploitability metrics (attack vector,\ncomplexity, privileges, and user interaction), impact metrics (confidentiality,\nintegrity, and availability), and scope. For example (https://www.first.org/cvss/specification-document)', - example: 5.5, - default_field: false, - }, - { - name: 'score.environmental', - level: 'extended', - type: 'float', - description: - 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe.\n\nEnvironmental scores cover an assessment for any modified Base metrics, confidentiality,\nintegrity, and availability requirements. For example (https://www.first.org/cvss/specification-document)', - example: 5.5, - default_field: false, - }, - { - name: 'score.temporal', - level: 'extended', - type: 'float', - description: - 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe.\n\nTemporal scores cover an assessment for code maturity, remediation level,\nand confidence. For example (https://www.first.org/cvss/specification-document)', - default_field: false, - }, - { - name: 'score.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The National Vulnerability Database (NVD) provides qualitative\nseverity rankings of "Low", "Medium", and "High" for CVSS v2.0 base score\nranges in addition to the severity ratings for CVSS v3.0 as they are defined\nin the CVSS v3.0 specification.\n\nCVSS is owned and managed by FIRST.Org, Inc. (FIRST), a US-based non-profit\norganization, whose mission is to help computer security incident response\nteams across the world. For example (https://nvd.nist.gov/vuln-metrics/cvss)', - example: 2, - default_field: false, - }, - { - name: 'severity', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The severity of the vulnerability can help with metrics and internal\nprioritization regarding remediation. For example (https://nvd.nist.gov/vuln-metrics/cvss)', - example: 'Critical', - default_field: false, - }, - ], - }, - ], - }, -]; diff --git a/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/filebeat.ts b/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/filebeat.ts deleted file mode 100644 index 3b8c92ebba2694..00000000000000 --- a/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/filebeat.ts +++ /dev/null @@ -1,21243 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/** - * An instance of the unmodified schema exported from filebeat-8.0.0-SNAPSHOT-darwin-x86_64.tar.gz - * - */ - -import { Schema } from '../type'; - -export const filebeatSchema: Schema = [ - { - key: 'ecs', - title: 'ECS', - description: 'ECS Fields.', - fields: [ - { - name: '@timestamp', - level: 'core', - required: true, - type: 'date', - description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', - example: '2016-05-23T08:05:34.853Z', - }, - { - name: 'labels', - level: 'core', - type: 'object', - object_type: 'keyword', - description: - 'Custom key/value pairs.\n\nCan be used to add meta information to events. Should not contain nested objects.\nAll values are stored as keyword.\n\nExample: `docker` and `k8s` labels.', - example: '{"application": "foo-bar", "env": "production"}', - }, - { - name: 'message', - level: 'core', - type: 'text', - description: - 'For log events the message field contains the log message, optimized\nfor viewing in a log viewer.\n\nFor structured logs without an original message field, other fields can be concatenated\nto form a human-readable summary of the event.\n\nIf multiple messages exist, they can be combined into one message.', - example: 'Hello World', - }, - { - name: 'tags', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - }, - { - name: 'agent', - title: 'Agent', - group: 2, - description: - 'The agent fields contain the data about the software entity, if\nany, that collects, detects, or observes events on a host, or takes measurements\non a host.\n\nExamples include Beats. Agents may also run on observers. ECS agent.* fields\nshall be populated with details of the agent running on the host or observer\nwhere the event happened or the measurement was taken.', - footnote: - 'Examples: In the case of Beats for logs, the agent.name is filebeat.\nFor APM, it is the agent running in the app/service. The agent information does\nnot change if data is sent through queuing systems like Kafka, Redis, or processing\nsystems such as Logstash or APM Server.', - type: 'group', - fields: [ - { - name: 'ephemeral_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', - example: '8a4f500d', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', - example: 'foo', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', - example: 'filebeat', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Version of the agent.', - example: '6.0.0-rc2', - }, - ], - }, - { - name: 'as', - title: 'Autonomous System', - group: 2, - description: - 'An autonomous system (AS) is a collection of connected Internet Protocol\n(IP) routing prefixes under the control of one or more network operators on\nbehalf of a single administrative entity or domain that presents a common, clearly\ndefined routing policy to the internet.', - type: 'group', - fields: [ - { - name: 'number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - ], - }, - { - name: 'client', - title: 'Client', - group: 2, - description: - 'A client is defined as the initiator of a network connection for\nevents regarding sessions, connections, or bidirectional flow records.\n\nFor TCP events, the client is the initiator of the TCP connection that sends\nthe SYN packet(s). For other protocols, the client is generally the initiator\nor requestor in the network transaction. Some systems use the term "originator"\nto refer the client in TCP connections. The client fields describe details about\nthe system acting as the client in the network event. Client fields are usually\npopulated in conjunction with server fields. Client fields are generally not\npopulated for packet-level events.\n\nClient / server representations can add semantic context to an exchange, which\nis helpful to visualize the data in certain situations. If your context falls\nin that category, you should still ensure that source and destination are filled\nappropriately.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event client addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the client to the server.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Client domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the client.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the client.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated IP of source based NAT sessions (e.g. internal client\nto internet).\n\nTypically connections traversing load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Translated port of source based NAT sessions (e.g. internal client\nto internet).\n\nTypically connections traversing load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the client to the server.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the client.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered client domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'cloud', - title: 'Cloud', - group: 2, - description: 'Fields related to the cloud or infrastructure the events are coming\nfrom.', - footnote: - 'Examples: If Metricbeat is running on an EC2 host and fetches data\nfrom its host, the cloud info contains the data about this machine. If Metricbeat\nruns on a remote machine outside the cloud and fetches data from a service running\nin the cloud, the field contains cloud data from the machine the service is\nrunning on.', - type: 'group', - fields: [ - { - name: 'account.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The cloud account or organization id used to identify different\nentities in a multi-tenant environment.\n\nExamples: AWS account id, Google Cloud ORG Id, or other unique identifier.', - example: 666777888999, - }, - { - name: 'availability_zone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Availability zone in which this host is running.', - example: 'us-east-1c', - }, - { - name: 'instance.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Instance ID of the host machine.', - example: 'i-1234567890abcdef0', - }, - { - name: 'instance.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Instance name of the host machine.', - }, - { - name: 'machine.type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Machine type of the host machine.', - example: 't2.medium', - }, - { - name: 'provider', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the cloud provider. Example values are aws, azure, gcp,\nor digitalocean.', - example: 'aws', - }, - { - name: 'region', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Region in which this host is running.', - example: 'us-east-1', - }, - ], - }, - { - name: 'code_signature', - title: 'Code Signature', - group: 2, - description: 'These fields contain information about binary code signatures.', - type: 'group', - fields: [ - { - name: 'exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - ], - }, - { - name: 'container', - title: 'Container', - group: 2, - description: - 'Container fields are used for meta information about the specific\ncontainer that is the source of information.\n\nThese fields help correlate data based containers from any runtime.', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique container id.', - }, - { - name: 'image.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the image the container was built on.', - }, - { - name: 'image.tag', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Container image tags.', - }, - { - name: 'labels', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: 'Image labels.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Container name.', - }, - { - name: 'runtime', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Runtime managing this container.', - example: 'docker', - }, - ], - }, - { - name: 'destination', - title: 'Destination', - group: 2, - description: - 'Destination fields describe details about the destination of a packet/event.\n\nDestination fields are usually populated in conjunction with source fields.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event destination addresses are defined ambiguously. The\nevent will sometimes list an IP, a domain or a unix socket. You should always\nstore the raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the destination to the source.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Destination domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the destination.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the destination.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated ip of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Port the source session is translated to by NAT Device.\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the destination to the source.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the destination.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered destination domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'dll', - title: 'DLL', - group: 2, - description: - 'These fields contain information about code libraries dynamically\nloaded into processes.\n\n\nMany operating systems refer to "shared code libraries" with different names,\nbut this field set refers to all of the following:\n\n* Dynamic-link library (`.dll`) commonly used on Windows\n\n* Shared Object (`.so`) commonly used on Unix-like operating systems\n\n* Dynamic library (`.dylib`) commonly used on macOS', - type: 'group', - fields: [ - { - name: 'code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - default_field: false, - }, - { - name: 'hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - default_field: false, - }, - { - name: 'hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - default_field: false, - }, - { - name: 'hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - default_field: false, - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the library.\n\nThis generally maps to the name of the file on disk.', - example: 'kernel32.dll', - default_field: false, - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Full file path of the library.', - example: 'C:\\Windows\\System32\\kernel32.dll', - default_field: false, - }, - { - name: 'pe.company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'pe.description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'pe.file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'pe.original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'pe.product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - ], - }, - { - name: 'dns', - title: 'DNS', - group: 2, - description: - 'Fields describing DNS queries and answers.\n\nDNS events should either represent a single DNS query prior to getting answers\n(`dns.type:query`) or they should represent a full exchange and contain the\nquery details as well as all of the answers that were provided for this query\n(`dns.type:answer`).', - type: 'group', - fields: [ - { - name: 'answers', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'An array containing an object for each answer section returned\nby the server.\n\nThe main keys that should be present in these objects are defined by ECS.\nRecords that have more information may contain more keys than what ECS defines.\n\nNot all DNS data sources give all details about DNS answers. At minimum, answer\nobjects must contain the `data` key. If more information is available, map\nas much of it to ECS as possible, and add any additional fields to the answer\nobjects as custom fields.', - }, - { - name: 'answers.class', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The class of DNS data contained in this resource record.', - example: 'IN', - }, - { - name: 'answers.data', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The data describing the resource.\n\nThe meaning of this data depends on the type and class of the resource record.', - example: '10.10.10.10', - }, - { - name: 'answers.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The domain name to which this resource record pertains.\n\nIf a chain of CNAME is being resolved, each answer `name` should be the\none that corresponds with the answer `data`. It should not simply be the\noriginal `question.name` repeated.', - example: 'www.google.com', - }, - { - name: 'answers.ttl', - level: 'extended', - type: 'long', - description: - 'The time interval in seconds that this resource record may be cached\nbefore it should be discarded. Zero values mean that the data should not be\ncached.', - example: 180, - }, - { - name: 'answers.type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The type of data contained in this resource record.', - example: 'CNAME', - }, - { - name: 'header_flags', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of 2 letter DNS header flags.\n\nExpected values are: AA, TC, RD, RA, AD, CD, DO.', - example: ['RD', 'RA'], - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The DNS packet identifier assigned by the program that generated\nthe query. The identifier is copied to the response.', - example: 62111, - }, - { - name: 'op_code', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The DNS operation code that specifies the kind of query in the\nmessage. This value is set by the originator of a query and copied into the\nresponse.', - example: 'QUERY', - }, - { - name: 'question.class', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The class of records being queried.', - example: 'IN', - }, - { - name: 'question.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The name being queried.\n\nIf the name field contains non-printable characters (below 32 or above 126),\nthose characters should be represented as escaped base 10 integers (\\DDD).\nBack slashes and quotes should be escaped. Tabs, carriage returns, and line\nfeeds should be converted to \\t, \\r, and \\n respectively.', - example: 'www.google.com', - }, - { - name: 'question.registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'question.subdomain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The subdomain is all of the labels under the registered_domain.\n\nIf the domain has multiple levels of subdomain, such as "sub2.sub1.example.com",\nthe subdomain field should contain "sub2.sub1", with no trailing period.', - example: 'www', - }, - { - name: 'question.top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'question.type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The type of record being queried.', - example: 'AAAA', - }, - { - name: 'resolved_ip', - level: 'extended', - type: 'ip', - description: - 'Array containing all IPs seen in `answers.data`.\n\nThe `answers` array can be difficult to use, because of the variety of data\nformats it can contain. Extracting all IP addresses seen in there to `dns.resolved_ip`\nmakes it possible to index them as IP addresses, and makes them easier to\nvisualize and query for.', - example: ['10.10.10.10', '10.10.10.11'], - }, - { - name: 'response_code', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The DNS response code.', - example: 'NOERROR', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of DNS event captured, query or answer.\n\nIf your source of DNS events only gives you DNS queries, you should only create\ndns events of type `dns.type:query`.\n\nIf your source of DNS events gives you answers as well, you should create\none event per query (optionally as soon as the query is seen). And a second\nevent containing all query details as well as an array of answers.', - example: 'answer', - }, - ], - }, - { - name: 'ecs', - title: 'ECS', - group: 2, - description: 'Meta-information specific to ECS.', - type: 'group', - fields: [ - { - name: 'version', - level: 'core', - required: true, - type: 'keyword', - ignore_above: 1024, - description: - 'ECS version this event conforms to. `ecs.version` is a required\nfield and must exist in all events.\n\nWhen querying across multiple indices -- which may conform to slightly different\nECS versions -- this field lets integrations adjust to the schema version\nof the events.', - example: '1.0.0', - }, - ], - }, - { - name: 'error', - title: 'Error', - group: 2, - description: - 'These fields can represent errors of any kind.\n\nUse them for errors that happen while fetching events or in cases where the\nevent itself contains an error.', - type: 'group', - fields: [ - { - name: 'code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Error code describing the error.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the error.', - }, - { - name: 'message', - level: 'core', - type: 'text', - description: 'Error message.', - }, - { - name: 'stack_trace', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The stack trace of this error in plain text.', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The type of the error, for example the class name of the exception.', - example: 'java.lang.NullPointerException', - }, - ], - }, - { - name: 'event', - title: 'Event', - group: 2, - description: - 'The event fields are used for context information about the log\nor metric event itself.\n\nA log is defined as an event containing details of something that happened.\nLog events must include the time at which the thing happened. Examples of log\nevents include a process starting on a host, a network packet being sent from\na source to a destination, or a network connection between a client and a server\nbeing initiated or closed. A metric is defined as an event containing one or\nmore numerical measurements and the time at which the measurement was taken.\nExamples of metric events include memory pressure measured on a host and device\ntemperature. See the `event.kind` definition in this section for additional\ndetails about metric and state events.', - type: 'group', - fields: [ - { - name: 'action', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The action captured by the event.\n\nThis describes the information in the event. It is more specific than `event.category`.\nExamples are `group-add`, `process-started`, `file-created`. The value is\nnormally defined by the implementer.', - example: 'user-password-change', - }, - { - name: 'category', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nsecond level in the ECS category hierarchy.\n\n`event.category` represents the "big buckets" of ECS categories. For example,\nfiltering on `event.category:process` yields all events relating to process\nactivity. This field is closely related to `event.type`, which is used as\na subcategory.\n\nThis field is an array. This will allow proper categorization of some events\nthat fall in multiple categories.', - example: 'authentication', - }, - { - name: 'code', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Identification code for this event, if one exists.\n\nSome event sources use event codes to identify messages unambiguously, regardless\nof message language or wording adjustments over time. An example of this is\nthe Windows Event ID.', - example: 4648, - }, - { - name: 'created', - level: 'core', - type: 'date', - description: - 'event.created contains the date/time when the event was first\nread by an agent, or by your pipeline.\n\nThis field is distinct from @timestamp in that @timestamp typically contain\nthe time extracted from the original event.\n\nIn most situations, these two timestamps will be slightly different. The difference\ncan be used to calculate the delay between your source generating an event,\nand the time when your agent first processed it. This can be used to monitor\nyour agent or pipeline ability to keep up with your event source.\n\nIn case the two timestamps are identical, @timestamp should be used.', - example: '2016-05-23T08:05:34.857Z', - }, - { - name: 'dataset', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the dataset.\n\nIf an event source publishes more than one type of log or events (e.g. access\nlog, error log), the dataset is used to specify which one the event comes\nfrom.\n\nIt is recommended but not required to start the dataset name with the module\nname, followed by a dot, then the dataset name.', - example: 'apache.access', - }, - { - name: 'duration', - level: 'core', - type: 'long', - format: 'duration', - input_format: 'nanoseconds', - output_format: 'asMilliseconds', - output_precision: 1, - description: - 'Duration of the event in nanoseconds.\n\nIf event.start and event.end are known this value should be the difference\nbetween the end and start time.', - }, - { - name: 'end', - level: 'extended', - type: 'date', - description: - 'event.end contains the date when the event ended or when the activity\nwas last observed.', - }, - { - name: 'hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Hash (perhaps logstash fingerprint) of raw field to be able to\ndemonstrate log integrity.', - example: '123456789012345678901234567890ABCD', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique ID to describe the event.', - example: '8a4f500d', - }, - { - name: 'ingested', - level: 'core', - type: 'date', - description: - 'Timestamp when an event arrived in the central data store.\n\nThis is different from `@timestamp`, which is when the event originally occurred. It is\nalso different from `event.created`, which is meant to capture the first time\nan agent saw the event.\n\nIn normal conditions, assuming no tampering, the timestamps should chronologically\nlook like this: `@timestamp` < `event.created` < `event.ingested`.', - example: '2016-05-23T08:05:35.101Z', - default_field: false, - }, - { - name: 'kind', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nhighest level in the ECS category hierarchy.\n\n`event.kind` gives high-level information about what type of information the\nevent contains, without being specific to the contents of the event. For example,\nvalues of this field distinguish alert events from metric events.\n\nThe value of this field can be used to inform how these kinds of events should\nbe handled. They may warrant different retention, different access control,\nit may also help understand whether the data coming in at a regular interval\nor not.', - example: 'alert', - }, - { - name: 'module', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the module this data is coming from.\n\nIf your monitoring agent supports the concept of modules or plugins to process\nevents of a given source (e.g. Apache logs), `event.module` should contain\nthe name of this module.', - example: 'apache', - }, - { - name: 'original', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Raw text message of entire event. Used to demonstrate log integrity.\n\nThis field is not indexed and doc_values are disabled. It cannot be searched,\nbut it can be retrieved from `_source`.', - example: - 'Sep 19 08:26:10 host CEF:0|Security| threatmanager|1.0|100|\nworm successfully stopped|10|src=10.0.0.1 dst=2.1.2.2spt=1232', - }, - { - name: 'outcome', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nlowest level in the ECS category hierarchy.\n\n`event.outcome` simply denotes whether the event represents a success or a\nfailure from the perspective of the entity that produced the event.\n\nNote that when a single transaction is described in multiple events, each\nevent may populate different values of `event.outcome`, according to their\nperspective.\n\nAlso note that in the case of a compound event (a single event that contains\nmultiple logical events), this field should be populated with the value that\nbest captures the overall success or failure from the perspective of the event\nproducer.\n\nFurther note that not all events will have an associated outcome. For example,\nthis field is generally not populated for metric events, events with `event.type:info`,\nor any events for which an outcome does not make logical sense.', - example: 'success', - }, - { - name: 'provider', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Source of the event.\n\nEvent transports such as Syslog or the Windows Event Log typically mention\nthe source of an event. It can be the name of the software that generated\nthe event (e.g. Sysmon, httpd), or of a subsystem of the operating system\n(kernel, Microsoft-Windows-Security-Auditing).', - example: 'kernel', - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Reference URL linking to additional information about this event.\n\nThis URL links to a static definition of the this event. Alert events, indicated\nby `event.kind:alert`, are a common use case for this field.', - example: 'https://system.vendor.com/event/#0001234', - default_field: false, - }, - { - name: 'risk_score', - level: 'core', - type: 'float', - description: - "Risk score or priority of the event (e.g. security solutions).\nUse your system's original value here.", - }, - { - name: 'risk_score_norm', - level: 'extended', - type: 'float', - description: - 'Normalized risk score or priority of the event, on a scale of\n0 to 100.\n\nThis is mainly useful if you use more than one system that assigns risk scores,\nand you want to see a normalized value across all systems.', - }, - { - name: 'sequence', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Sequence number of the event.\n\nThe sequence number is a value published by some event sources, to make the\nexact ordering of events unambiguous, regardless of the timestamp precision.', - }, - { - name: 'severity', - level: 'core', - type: 'long', - format: 'string', - description: - 'The numeric severity of the event according to your event source.\n\nWhat the different severity values mean can be different between sources and\nuse cases. It is up to the implementer to make sure severities are consistent\nacross events from the same source.\n\nThe Syslog severity belongs in `log.syslog.severity.code`. `event.severity`\nis meant to represent the severity according to the event source (e.g. firewall,\nIDS). If the event source does not publish its own severity, you may optionally\ncopy the `log.syslog.severity.code` to `event.severity`.', - example: 7, - }, - { - name: 'start', - level: 'extended', - type: 'date', - description: - 'event.start contains the date when the event started or when the\nactivity was first observed.', - }, - { - name: 'timezone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'This field should be populated when the event timestamp does\nnot include timezone information already (e.g. default Syslog timestamps).\nIt is optional otherwise.\n\nAcceptable timezone formats are: a canonical ID (e.g. "Europe/Amsterdam"),\nabbreviated (e.g. "EST") or an HH:mm differential (e.g. "-05:00").', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nthird level in the ECS category hierarchy.\n\n`event.type` represents a categorization "sub-bucket" that, when used along\nwith the `event.category` field values, enables filtering events down to a\nlevel appropriate for single visualization.\n\nThis field is an array. This will allow proper categorization of some events\nthat fall in multiple event types.', - }, - { - name: 'url', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'URL linking to an external system to continue investigation of\nthis event.\n\nThis URL links to another system where in-depth investigation of the specific\noccurence of this event can take place. Alert events, indicated by `event.kind:alert`,\nare a common use case for this field.', - example: 'https://mysystem.mydomain.com/alert/5271dedb-f5b0-4218-87f0-4ac4870a38fe', - default_field: false, - }, - ], - }, - { - name: 'file', - title: 'File', - group: 2, - description: - 'A file is defined as a set of information that has been created\non, or has existed on a filesystem.\n\nFile objects can be associated with host events, network events, and/or file\nevents (e.g., those produced by File Integrity Monitoring [FIM] products or\nservices). File fields provide details about the affected file associated with\nthe event or metric.', - type: 'group', - fields: [ - { - name: 'accessed', - level: 'extended', - type: 'date', - description: - 'Last time the file was accessed.\n\nNote that not all filesystems keep track of access time.', - }, - { - name: 'attributes', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of file attributes.\n\nAttributes names will vary by platform. Here is a non-exhaustive list of values\nthat are expected in this field: archive, compressed, directory, encrypted,\nexecute, hidden, read, readonly, system, write.', - example: '["readonly", "system"]', - default_field: false, - }, - { - name: 'code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'created', - level: 'extended', - type: 'date', - description: - 'File creation time.\n\nNote that not all filesystems store the creation time.', - }, - { - name: 'ctime', - level: 'extended', - type: 'date', - description: - 'Last time the file attributes or metadata changed.\n\nNote that changes to the file content will update `mtime`. This implies `ctime`\nwill be adjusted at the same time, since `mtime` is an attribute of the file.', - }, - { - name: 'device', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Device that is the source of the file.', - example: 'sda', - }, - { - name: 'directory', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Directory where the file is located. It should include the drive\nletter, when appropriate.', - example: '/home/alice', - }, - { - name: 'drive_letter', - level: 'extended', - type: 'keyword', - ignore_above: 1, - description: - 'Drive letter where the file is located. This field is only relevant\non Windows.\n\nThe value should be uppercase, and not include the colon.', - example: 'C', - default_field: false, - }, - { - name: 'extension', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'File extension.', - example: 'png', - }, - { - name: 'gid', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Primary group ID (GID) of the file.', - example: '1001', - }, - { - name: 'group', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Primary group name of the file.', - example: 'alice', - }, - { - name: 'hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - }, - { - name: 'hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - }, - { - name: 'hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - }, - { - name: 'hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - }, - { - name: 'inode', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Inode representing the file in the filesystem.', - example: '256383', - }, - { - name: 'mime_type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'MIME type should identify the format of the file or stream of bytes\nusing https://www.iana.org/assignments/media-types/media-types.xhtml[IANA\nofficial types], where possible. When more than one type is applicable, the\nmost specific type should be used.', - default_field: false, - }, - { - name: 'mode', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Mode of the file in octal representation.', - example: '0640', - }, - { - name: 'mtime', - level: 'extended', - type: 'date', - description: 'Last time the file content was modified.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the file including the extension, without the directory.', - example: 'example.png', - }, - { - name: 'owner', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: "File owner's username.", - example: 'alice', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'Full path to the file, including the file name. It should include\nthe drive letter, when appropriate.', - example: '/home/alice/example.png', - }, - { - name: 'pe.company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'pe.description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'pe.file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'pe.original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'pe.product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - { - name: 'size', - level: 'extended', - type: 'long', - description: 'File size in bytes.\n\nOnly relevant when `file.type` is "file".', - example: 16384, - }, - { - name: 'target_path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Target path for symlinks.', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'File type (file, dir, or symlink).', - example: 'file', - }, - { - name: 'uid', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The user ID (UID) or security identifier (SID) of the file owner.', - example: '1001', - }, - ], - }, - { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an\nevent.\n\nThis geolocation information can be derived from techniques such as Geo IP,\nor be user-supplied.', - type: 'group', - fields: [ - { - name: 'city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - ], - }, - { - name: 'group', - title: 'Group', - group: 2, - description: - 'The group fields are meant to represent groups that are relevant\nto the event.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - ], - }, - { - name: 'hash', - title: 'Hash', - group: 2, - description: - 'The hash fields represent different hash algorithms and their values.\n\nField names for common hashes (e.g. MD5, SHA1) are predefined. Add fields for\nother hashes by lowercasing the hash algorithm name and using underscore separators\nas appropriate (snake case, e.g. sha3_512).', - type: 'group', - fields: [ - { - name: 'md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - }, - { - name: 'sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - }, - { - name: 'sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - }, - { - name: 'sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - }, - ], - }, - { - name: 'host', - title: 'Host', - group: 2, - description: - 'A host is defined as a general computing instance.\n\nECS host.* fields should be populated with details about the host on which the\nevent happened, or from which the measurement was taken. Host types include\nhardware, virtual machines, Docker containers, and Kubernetes nodes.', - type: 'group', - fields: [ - { - name: 'architecture', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system architecture.', - example: 'x86_64', - }, - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the domain of which the host is a member.\n\nFor example, on Windows this could be the host Active Directory domain\nor NetBIOS domain name. For Linux this could be the domain of the host\nLDAP provider.', - example: 'CONTOSO', - default_field: false, - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'hostname', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Hostname of the host.\n\nIt normally contains what the `hostname` command returns on the host machine.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique host id.\n\nAs hostname is not always unique, use values that are meaningful in your environment.\n\nExample: The current usage of `beat.name`.', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'Host ip addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Host mac addresses.', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Type of host.\n\nFor Cloud providers this can be the machine type like `t2.medium`. If vm,\nthis could be the container, for example, or other information meaningful\nin your environment.', - }, - { - name: 'uptime', - level: 'extended', - type: 'long', - description: 'Seconds the host has been up.', - example: 1325, - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'http', - title: 'HTTP', - group: 2, - description: - 'Fields related to HTTP activity. Use the `url` field set to store\nthe url of the request.', - type: 'group', - fields: [ - { - name: 'request.body.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the request body.', - example: 887, - }, - { - name: 'request.body.content', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The full HTTP request body.', - example: 'Hello world', - }, - { - name: 'request.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the request (body and headers).', - example: 1437, - }, - { - name: 'request.method', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'HTTP request method.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'get, post, put', - }, - { - name: 'request.referrer', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Referrer for this HTTP request.', - example: 'https://blog.example.com/', - }, - { - name: 'response.body.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the response body.', - example: 887, - }, - { - name: 'response.body.content', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The full HTTP response body.', - example: 'Hello world', - }, - { - name: 'response.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the response (body and headers).', - example: 1437, - }, - { - name: 'response.status_code', - level: 'extended', - type: 'long', - format: 'string', - description: 'HTTP response status code.', - example: 404, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'HTTP version.', - example: 1.1, - }, - ], - }, - { - name: 'interface', - title: 'Interface', - group: 2, - description: - 'The interface fields are used to record ingress and egress interface\ninformation when reported by an observer (e.g. firewall, router, load balancer)\nin the context of the observer handling a network connection. In the case of\na single observer interface (e.g. network sensor on a span port) only the observer.ingress\ninformation should be populated.', - type: 'group', - fields: [ - { - name: 'alias', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Interface alias as reported by the system, typically used in firewall\nimplementations for e.g. inside, outside, or dmz logical interface naming.', - example: 'outside', - default_field: false, - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface ID as reported by an observer (typically SNMP interface\nID).', - example: 10, - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface name as reported by the system.', - example: 'eth0', - default_field: false, - }, - ], - }, - { - name: 'log', - title: 'Log', - group: 2, - description: - 'Details about the event logging mechanism or logging transport.\n\nThe log.* fields are typically populated with details about the logging mechanism\nused to create and/or transport the event. For example, syslog details belong\nunder `log.syslog.*`.\n\nThe details specific to your event source are typically not logged under `log.*`,\nbut rather in `event.*` or in other ECS fields.', - type: 'group', - fields: [ - { - name: 'level', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Original log level of the log event.\n\nIf the source of the event provides a log level or textual severity, this\nis the one that goes in `log.level`. If your source does not specify one,\nyou may put your event transport severity here (e.g. Syslog severity).\n\nSome examples are `warn`, `err`, `i`, `informational`.', - example: 'error', - }, - { - name: 'logger', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The name of the logger inside an application. This is usually the\nname of the class which initialized the logger, or can be a custom name.', - example: 'org.elasticsearch.bootstrap.Bootstrap', - }, - { - name: 'origin.file.line', - level: 'extended', - type: 'integer', - description: - 'The line number of the file containing the source code which originated\nthe log event.', - example: 42, - }, - { - name: 'origin.file.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The name of the file containing the source code which originated\nthe log event. Note that this is not the name of the log file.', - example: 'Bootstrap.java', - }, - { - name: 'origin.function', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The name of the function or method which originated the log event.', - example: 'init', - }, - { - name: 'original', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is the original log message and contains the full log message\nbefore splitting it up in multiple parts.\n\nIn contrast to the `message` field which can contain an extracted part of\nthe log message, this field contains the original, full log message. It can\nhave already some modifications applied like encoding or new lines removed\nto clean up the log message.\n\nThis field is not indexed and doc_values are disabled so it cannot be queried\nbut the value can be retrieved from `_source`.', - example: 'Sep 19 08:26:10 localhost My log', - }, - { - name: 'syslog', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'The Syslog metadata of the event, if the event was transmitted\nvia Syslog. Please see RFCs 5424 or 3164.', - }, - { - name: 'syslog.facility.code', - level: 'extended', - type: 'long', - format: 'string', - description: - 'The Syslog numeric facility of the log event, if available.\n\nAccording to RFCs 5424 and 3164, this value should be an integer between 0\nand 23.', - example: 23, - }, - { - name: 'syslog.facility.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The Syslog text-based facility of the log event, if available.', - example: 'local7', - }, - { - name: 'syslog.priority', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Syslog numeric priority of the event, if available.\n\nAccording to RFCs 5424 and 3164, the priority is 8 * facility + severity.\nThis number is therefore expected to contain a value between 0 and 191.', - example: 135, - }, - { - name: 'syslog.severity.code', - level: 'extended', - type: 'long', - description: - 'The Syslog numeric severity of the log event, if available.\n\nIf the event source publishing via Syslog provides a different numeric severity\nvalue (e.g. firewall, IDS), your source numeric severity should go to `event.severity`.\nIf the event source does not specify a distinct severity, you can optionally\ncopy the Syslog severity to `event.severity`.', - example: 3, - }, - { - name: 'syslog.severity.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The Syslog numeric severity of the log event, if available.\n\nIf the event source publishing via Syslog provides a different severity value\n(e.g. firewall, IDS), your source text severity should go to `log.level`.\nIf the event source does not specify a distinct severity, you can optionally\ncopy the Syslog severity to `log.level`.', - example: 'Error', - }, - ], - }, - { - name: 'network', - title: 'Network', - group: 2, - description: - 'The network is defined as the communication path over which a host\nor network event happens.\n\nThe network.* fields should be populated with details about the network activity\nassociated with an event.', - type: 'group', - fields: [ - { - name: 'application', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A name given to an application level protocol. This can be arbitrarily\nassigned for things like microservices, but also apply to things like skype,\nicq, facebook, twitter. This would be used in situations where the vendor\nor service can be decoded such as from the source/dest IP owners, ports, or\nwire format.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'aim', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: - 'Total bytes transferred in both directions.\n\nIf `source.bytes` and `destination.bytes` are known, `network.bytes` is their\nsum.', - example: 368, - }, - { - name: 'community_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash of source and destination IPs and ports, as well as the\nprotocol used in a communication. This is a tool-agnostic standard to identify\nflows.\n\nLearn more at https://github.com/corelight/community-id-spec.', - example: '1:hO+sN4H+MG5MY/8hIrXPqc4ZQz0=', - }, - { - name: 'direction', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - "Direction of the network traffic.\nRecommended values are:\n * inbound\n * outbound\n * internal\n * external\n * unknown\n\nWhen mapping events from a host-based monitoring context, populate this field from the host's point of view.\nWhen mapping events from a network or perimeter-based monitoring context, populate this field from the point of view of your network perimeter.", - example: 'inbound', - }, - { - name: 'forwarded_ip', - level: 'core', - type: 'ip', - description: 'Host IP address when the source IP address is the proxy.', - example: '192.1.1.2', - }, - { - name: 'iana_number', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'IANA Protocol Number (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml).\nStandardized list of protocols. This aligns well with NetFlow and sFlow related\nlogs which use the IANA Protocol Number.', - example: 6, - }, - { - name: 'inner', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'Network.inner fields are added in addition to network.vlan fields\nto describe the innermost VLAN when q-in-q VLAN tagging is present. Allowed\nfields include vlan.id and vlan.name. Inner vlan fields are typically used\nwhen sending traffic with multiple 802.1q encapsulations to a network sensor\n(e.g. Zeek, Wireshark.)', - default_field: false, - }, - { - name: 'inner.vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'inner.vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name given by operators to sections of their network.', - example: 'Guest Wifi', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: - 'Total packets transferred in both directions.\n\nIf `source.packets` and `destination.packets` are known, `network.packets`\nis their sum.', - example: 24, - }, - { - name: 'protocol', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'L7 Network protocol name. ex. http, lumberjack, transport protocol.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'http', - }, - { - name: 'transport', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Same as network.iana_number, but instead using the Keyword name\nof the transport layer (udp, tcp, ipv6-icmp, etc.)\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'tcp', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'In the OSI Model this would be the Network Layer. ipv4, ipv6,\nipsec, pim, etc\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'ipv4', - }, - { - name: 'vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - ], - }, - { - name: 'observer', - title: 'Observer', - group: 2, - description: - 'An observer is defined as a special network, security, or application\ndevice used to detect, observe, or create network, security, or application-related\nevents and metrics.\n\nThis could be a custom hardware appliance or a server that has been configured\nto run special network, security, or application software. Examples include\nfirewalls, web proxies, intrusion detection/prevention systems, network monitoring\nsensors, web application firewalls, data loss prevention systems, and APM servers.\nThe observer.* fields shall be populated with details of the system, if any,\nthat detects, observes and/or creates a network, security, or application event\nor metric. Message queues and ETL components used in processing events or metrics\nare not considered observers in ECS.', - type: 'group', - fields: [ - { - name: 'egress', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'Observer.egress holds information like interface number and name,\nvlan, and zone information to classify egress traffic. Single armed monitoring\nsuch as a network sensor on a span port should only use observer.ingress\nto categorize traffic.', - default_field: false, - }, - { - name: 'egress.interface.alias', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Interface alias as reported by the system, typically used in firewall\nimplementations for e.g. inside, outside, or dmz logical interface naming.', - example: 'outside', - default_field: false, - }, - { - name: 'egress.interface.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface ID as reported by an observer (typically SNMP interface\nID).', - example: 10, - default_field: false, - }, - { - name: 'egress.interface.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface name as reported by the system.', - example: 'eth0', - default_field: false, - }, - { - name: 'egress.vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'egress.vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - { - name: 'egress.zone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Network zone of outbound traffic as reported by the observer to\ncategorize the destination area of egress traffic, e.g. Internal, External,\nDMZ, HR, Legal, etc.', - example: 'Public_Internet', - default_field: false, - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'hostname', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Hostname of the observer.', - }, - { - name: 'ingress', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'Observer.ingress holds information like interface number and name,\nvlan, and zone information to classify ingress traffic. Single armed monitoring\nsuch as a network sensor on a span port should only use observer.ingress\nto categorize traffic.', - default_field: false, - }, - { - name: 'ingress.interface.alias', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Interface alias as reported by the system, typically used in firewall\nimplementations for e.g. inside, outside, or dmz logical interface naming.', - example: 'outside', - default_field: false, - }, - { - name: 'ingress.interface.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface ID as reported by an observer (typically SNMP interface\nID).', - example: 10, - default_field: false, - }, - { - name: 'ingress.interface.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface name as reported by the system.', - example: 'eth0', - default_field: false, - }, - { - name: 'ingress.vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'ingress.vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - { - name: 'ingress.zone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Network zone of incoming traffic as reported by the observer to\ncategorize the source area of ingress traffic. e.g. internal, External, DMZ,\nHR, Legal, etc.', - example: 'DMZ', - default_field: false, - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'IP addresses of the observer.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC addresses of the observer', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Custom name of the observer.\n\nThis is a name that can be given to an observer. This can be helpful for example\nif multiple firewalls of the same model are used in an organization.\n\nIf no custom name is needed, the field can be left empty.', - example: '1_proxySG', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The product name of the observer.', - example: 's200', - }, - { - name: 'serial_number', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Observer serial number.', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of the observer the data is coming from.\n\nThere is no predefined list of observer types. Some examples are `forwarder`,\n`firewall`, `ids`, `ips`, `proxy`, `poller`, `sensor`, `APM server`.', - example: 'firewall', - }, - { - name: 'vendor', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Vendor name of the observer.', - example: 'Symantec', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Observer version.', - }, - ], - }, - { - name: 'organization', - title: 'Organization', - group: 2, - description: - 'The organization fields enrich data with information about the company\nor entity the data is associated with.\n\nThese fields help you arrange or filter data stored in an index by one or multiple\norganizations.', - type: 'group', - fields: [ - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the organization.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - }, - ], - }, - { - name: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - type: 'group', - fields: [ - { - name: 'family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - ], - }, - { - name: 'package', - title: 'Package', - group: 2, - description: - 'These fields contain information about an installed software package.\nIt contains general information about a package, such as name, version or size.\nIt also contains installation details, such as time or location.', - type: 'group', - fields: [ - { - name: 'architecture', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Package architecture.', - example: 'x86_64', - }, - { - name: 'build_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the build version of the installed\npackage.\n\nFor example use the commit SHA of a non-released package.', - example: '36f4f7e89dd61b0988b12ee000b98966867710cd', - default_field: false, - }, - { - name: 'checksum', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Checksum of the installed package for verification.', - example: '68b329da9893e34099c7d8ad5cb9c940', - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Description of the package.', - example: - 'Open source programming language to build simple/reliable/efficient\nsoftware.', - }, - { - name: 'install_scope', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Indicating how the package was installed, e.g. user-local, global.', - example: 'global', - }, - { - name: 'installed', - level: 'extended', - type: 'date', - description: 'Time when package was installed.', - }, - { - name: 'license', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'License under which the package was released.\n\nUse a short name, e.g. the license identifier from SPDX License List where\npossible (https://spdx.org/licenses/).', - example: 'Apache License 2.0', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Package name', - example: 'go', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Path where the package is installed.', - example: '/usr/local/Cellar/go/1.12.9/', - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Home page or reference URL of the software in this package, if\navailable.', - example: 'https://golang.org', - default_field: false, - }, - { - name: 'size', - level: 'extended', - type: 'long', - format: 'string', - description: 'Package size in bytes.', - example: 62231, - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Type of package.\n\nThis should contain the package file type, rather than the package manager\nname. Examples: rpm, dpkg, brew, npm, gem, nupkg, jar.', - example: 'rpm', - default_field: false, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Package version', - example: '1.12.9', - }, - ], - }, - { - name: 'pe', - title: 'PE Header', - group: 2, - description: 'These fields contain Windows Portable Executable (PE) metadata.', - type: 'group', - fields: [ - { - name: 'company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - ], - }, - { - name: 'process', - title: 'Process', - group: 2, - description: - 'These fields contain information about a process.\n\nThese fields can help you correlate metrics information with a process id/name\nfrom a log message. The `process.pid` often stays in the metric itself and\nis copied to the global field for correlation.', - type: 'group', - fields: [ - { - name: 'args', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.', - example: ['/usr/bin/ssh', '-l', 'user', '10.0.0.16'], - }, - { - name: 'args_count', - level: 'extended', - type: 'long', - description: - 'Length of the process.args array.\n\nThis field can be useful for querying or performing bucket analysis on how\nmany arguments were provided to start a process. More arguments may be an\nindication of suspicious activity.', - example: 4, - default_field: false, - }, - { - name: 'code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'command_line', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'Full command line that started the process, including the absolute\npath to the executable, and all arguments.\n\nSome arguments may be filtered to protect sensitive information.', - example: '/usr/bin/ssh -l user 10.0.0.16', - default_field: false, - }, - { - name: 'entity_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier for the process.\n\nThe implementation of this is specified by the data source, but some examples\nof what could be used here are a process-generated UUID, Sysmon Process GUIDs,\nor a hash of some uniquely identifying components of a process.\n\nConstructing a globally unique identifier is a common practice to mitigate\nPID reuse as well as to identify a specific process over time, across multiple\nmonitored hosts.', - example: 'c2c455d9f99375d', - default_field: false, - }, - { - name: 'executable', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', - }, - { - name: 'exit_code', - level: 'extended', - type: 'long', - description: - 'The exit code of the process, if this is a termination event.\n\nThe field should be absent if there is no exit code for the event (e.g. process\nstart).', - example: 137, - default_field: false, - }, - { - name: 'hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - }, - { - name: 'hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - }, - { - name: 'hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - }, - { - name: 'hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Process name.\n\nSometimes called program name or similar.', - example: 'ssh', - }, - { - name: 'parent.args', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of process arguments.\n\nMay be filtered to protect sensitive information.', - example: ['ssh', '-l', 'user', '10.0.0.16'], - default_field: false, - }, - { - name: 'parent.args_count', - level: 'extended', - type: 'long', - description: - 'Length of the process.args array.\n\nThis field can be useful for querying or performing bucket analysis on how\nmany arguments were provided to start a process. More arguments may be an\nindication of suspicious activity.', - example: 4, - default_field: false, - }, - { - name: 'parent.code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'parent.code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'parent.code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'parent.code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'parent.code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'parent.command_line', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'Full command line that started the process, including the absolute\npath to the executable, and all arguments.\n\nSome arguments may be filtered to protect sensitive information.', - example: '/usr/bin/ssh -l user 10.0.0.16', - default_field: false, - }, - { - name: 'parent.entity_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier for the process.\n\nThe implementation of this is specified by the data source, but some examples\nof what could be used here are a process-generated UUID, Sysmon Process GUIDs,\nor a hash of some uniquely identifying components of a process.\n\nConstructing a globally unique identifier is a common practice to mitigate\nPID reuse as well as to identify a specific process over time, across multiple\nmonitored hosts.', - example: 'c2c455d9f99375d', - default_field: false, - }, - { - name: 'parent.executable', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', - default_field: false, - }, - { - name: 'parent.exit_code', - level: 'extended', - type: 'long', - description: - 'The exit code of the process, if this is a termination event.\n\nThe field should be absent if there is no exit code for the event (e.g. process\nstart).', - example: 137, - default_field: false, - }, - { - name: 'parent.hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - default_field: false, - }, - { - name: 'parent.hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - default_field: false, - }, - { - name: 'parent.hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - default_field: false, - }, - { - name: 'parent.hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - default_field: false, - }, - { - name: 'parent.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'Process name.\n\nSometimes called program name or similar.', - example: 'ssh', - default_field: false, - }, - { - name: 'parent.pgid', - level: 'extended', - type: 'long', - format: 'string', - description: 'Identifier of the group of processes the process belongs to.', - default_field: false, - }, - { - name: 'parent.pid', - level: 'core', - type: 'long', - format: 'string', - description: 'Process id.', - example: 4242, - default_field: false, - }, - { - name: 'parent.ppid', - level: 'extended', - type: 'long', - format: 'string', - description: "Parent process' pid.", - example: 4241, - default_field: false, - }, - { - name: 'parent.start', - level: 'extended', - type: 'date', - description: 'The time the process started.', - example: '2016-05-23T08:05:34.853Z', - default_field: false, - }, - { - name: 'parent.thread.id', - level: 'extended', - type: 'long', - format: 'string', - description: 'Thread ID.', - example: 4242, - default_field: false, - }, - { - name: 'parent.thread.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Thread name.', - example: 'thread-0', - default_field: false, - }, - { - name: 'parent.title', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'Process title.\n\nThe proctitle, some times the same as process name. Can also be different:\nfor example a browser setting its title to the web page currently opened.', - default_field: false, - }, - { - name: 'parent.uptime', - level: 'extended', - type: 'long', - description: 'Seconds the process has been up.', - example: 1325, - default_field: false, - }, - { - name: 'parent.working_directory', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'The working directory of the process.', - example: '/home/alice', - default_field: false, - }, - { - name: 'pe.company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'pe.description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'pe.file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'pe.original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'pe.product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - { - name: 'pgid', - level: 'extended', - type: 'long', - format: 'string', - description: 'Identifier of the group of processes the process belongs to.', - }, - { - name: 'pid', - level: 'core', - type: 'long', - format: 'string', - description: 'Process id.', - example: 4242, - }, - { - name: 'ppid', - level: 'extended', - type: 'long', - format: 'string', - description: "Parent process' pid.", - example: 4241, - }, - { - name: 'start', - level: 'extended', - type: 'date', - description: 'The time the process started.', - example: '2016-05-23T08:05:34.853Z', - }, - { - name: 'thread.id', - level: 'extended', - type: 'long', - format: 'string', - description: 'Thread ID.', - example: 4242, - }, - { - name: 'thread.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Thread name.', - example: 'thread-0', - }, - { - name: 'title', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'Process title.\n\nThe proctitle, some times the same as process name. Can also be different:\nfor example a browser setting its title to the web page currently opened.', - }, - { - name: 'uptime', - level: 'extended', - type: 'long', - description: 'Seconds the process has been up.', - example: 1325, - }, - { - name: 'working_directory', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The working directory of the process.', - example: '/home/alice', - }, - ], - }, - { - name: 'registry', - title: 'Registry', - group: 2, - description: 'Fields related to Windows Registry operations.', - type: 'group', - fields: [ - { - name: 'data.bytes', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Original bytes written with base64 encoding.\n\nFor Windows registry operations, such as SetValueEx and RegQueryValueEx, this\ncorresponds to the data pointed by `lp_data`. This is optional but provides\nbetter recoverability and should be populated for REG_BINARY encoded values.', - example: 'ZQBuAC0AVQBTAAAAZQBuAAAAAAA=', - default_field: false, - }, - { - name: 'data.strings', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Content when writing string types.\n\nPopulated as an array when writing string data to the registry. For single\nstring registry types (REG_SZ, REG_EXPAND_SZ), this should be an array with\none string. For sequences of string with REG_MULTI_SZ, this array will be\nvariable length. For numeric data, such as REG_DWORD and REG_QWORD, this should\nbe populated with the decimal representation (e.g `"1"`).', - example: '["C:\\rta\\red_ttp\\bin\\myapp.exe"]', - default_field: false, - }, - { - name: 'data.type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Standard registry type for encoding contents', - example: 'REG_SZ', - default_field: false, - }, - { - name: 'hive', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Abbreviated name for the hive.', - example: 'HKLM', - default_field: false, - }, - { - name: 'key', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Hive-relative path of keys.', - example: - 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\winword.exe', - default_field: false, - }, - { - name: 'path', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Full path, including hive, key and value', - example: - 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution\nOptions\\winword.exe\\Debugger', - default_field: false, - }, - { - name: 'value', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the value written.', - example: 'Debugger', - default_field: false, - }, - ], - }, - { - name: 'related', - title: 'Related', - group: 2, - description: - 'This field set is meant to facilitate pivoting around a piece of\ndata.\n\nSome pieces of information can be seen in many places in an ECS event. To facilitate\nsearching for them, store an array of all seen values to their corresponding\nfield in `related.`.\n\nA concrete example is IP addresses, which can be under host, observer, source,\ndestination, client, server, and network.forwarded_ip. If you append all IPs\nto `related.ip`, you can then search for a given IP trivially, no matter where\nit appeared, by querying `related.ip:192.0.2.15`.', - type: 'group', - fields: [ - { - name: 'hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - "All the hashes seen on your event. Populating this field, then\nusing it to search for hashes can help in situations where you're unsure what\nthe hash algorithm is (and therefore which key name to search).", - default_field: false, - }, - { - name: 'ip', - level: 'extended', - type: 'ip', - description: 'All of the IPs seen on your event.', - }, - { - name: 'user', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'All the user names seen on your event.', - default_field: false, - }, - ], - }, - { - name: 'rule', - title: 'Rule', - group: 2, - description: - 'Rule fields are used to capture the specifics of any observer or\nagent rules that generate alerts or other notable events.\n\nExamples of data sources that would populate the rule fields include: network\nadmission control platforms, network or host IDS/IPS, network firewalls, web\napplication firewalls, url filters, endpoint detection and response (EDR) systems,\netc.', - type: 'group', - fields: [ - { - name: 'author', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name, organization, or pseudonym of the author or authors who created\nthe rule used to generate this event.', - example: ['Star-Lord'], - default_field: false, - }, - { - name: 'category', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A categorization value keyword used by the entity using the rule\nfor detection of this event.', - example: 'Attempted Information Leak', - default_field: false, - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The description of the rule generating the event.', - example: 'Block requests to public DNS over HTTPS / TLS protocols', - default_field: false, - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A rule ID that is unique within the scope of an agent, observer,\nor other entity using the rule for detection of this event.', - example: 101, - default_field: false, - }, - { - name: 'license', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the license under which the rule used to generate this\nevent is made available.', - example: 'Apache 2.0', - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The name of the rule or signature generating the event.', - example: 'BLOCK_DNS_over_TLS', - default_field: false, - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Reference URL to additional information about the rule used to\ngenerate this event.\n\nThe URL can point to the vendor documentation about the rule. If that is\nnot available, it can also be a link to a more general page describing this\ntype of alert.', - example: 'https://en.wikipedia.org/wiki/DNS_over_TLS', - default_field: false, - }, - { - name: 'ruleset', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the ruleset, policy, group, or parent category in which\nthe rule used to generate this event is a member.', - example: 'Standard_Protocol_Filters', - default_field: false, - }, - { - name: 'uuid', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A rule ID that is unique within the scope of a set or group of\nagents, observers, or other entities using the rule for detection of this\nevent.', - example: 1100110011, - default_field: false, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The version / revision of the rule being used for analysis.', - example: 1.1, - default_field: false, - }, - ], - }, - { - name: 'server', - title: 'Server', - group: 2, - description: - 'A Server is defined as the responder in a network connection for\nevents regarding sessions, connections, or bidirectional flow records.\n\nFor TCP events, the server is the receiver of the initial SYN packet(s) of the\nTCP connection. For other protocols, the server is generally the responder in\nthe network transaction. Some systems actually use the term "responder" to refer\nthe server in TCP connections. The server fields describe details about the\nsystem acting as the server in the network event. Server fields are usually\npopulated in conjunction with client fields. Server fields are generally not\npopulated for packet-level events.\n\nClient / server representations can add semantic context to an exchange, which\nis helpful to visualize the data in certain situations. If your context falls\nin that category, you should still ensure that source and destination are filled\nappropriately.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event server addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the server to the client.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Server domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the server.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the server.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated ip of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Translated port of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the server to the client.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the server.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered server domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'service', - title: 'Service', - group: 2, - description: - 'The service fields describe the service for or from which the data\nwas collected.\n\nThese fields help you find and correlate logs for a specific service and version.', - type: 'group', - fields: [ - { - name: 'ephemeral_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Ephemeral identifier of this service (if one exists).\n\nThis id normally changes across restarts, but `service.id` does not.', - example: '8a4f500f', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of the running service. If the service is comprised\nof many nodes, the `service.id` should be the same for all nodes.\n\nThis id should uniquely identify the service. This makes it possible to correlate\nlogs and metrics for one specific service, no matter which particular node\nemitted the event.\n\nNote that if you need to see the events from one specific host of the service,\nyou should filter on that `host.name` or `host.id` instead.', - example: 'd37e5ebfe0ae6c4972dbe9f0174a1637bb8247f6', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the service data is collected from.\n\nThe name of the service is normally user given. This allows for distributed\nservices that run on multiple hosts to correlate the related instances based\non the name.\n\nIn the case of Elasticsearch the `service.name` could contain the cluster\nname. For Beats the `service.name` is by default a copy of the `service.type`\nfield if no name is specified.', - example: 'elasticsearch-metrics', - }, - { - name: 'node.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of a service node.\n\nThis allows for two nodes of the same service running on the same host to\nbe differentiated. Therefore, `service.node.name` should typically be unique\nacross nodes of a given service.\n\nIn the case of Elasticsearch, the `service.node.name` could contain the unique\nnode name within the Elasticsearch cluster. In cases where the service does not\nhave the concept of a node name, the host name or container name can be used\nto distinguish running instances that make up this service. If those do not\nprovide uniqueness (e.g. multiple instances of the service running on the\nsame host) - the node name can be manually set.', - example: 'instance-0000000016', - }, - { - name: 'state', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Current state of the service.', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of the service data is collected from.\n\nThe type can be used to group and correlate logs and metrics from one service\ntype.\n\nExample: If logs or metrics are collected from Elasticsearch, `service.type`\nwould be `elasticsearch`.', - example: 'elasticsearch', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Version of the service the data was collected from.\n\nThis allows to look at a data set only for a specific version of a service.', - example: '3.2.4', - }, - ], - }, - { - name: 'source', - title: 'Source', - group: 2, - description: - 'Source fields describe details about the source of a packet/event.\n\nSource fields are usually populated in conjunction with destination fields.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event source addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the source to the destination.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Source domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the source.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the source.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated ip of source based NAT sessions (e.g. internal client\nto internet)\n\nTypically connections traversing load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Translated port of source based NAT sessions. (e.g. internal client\nto internet)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the source to the destination.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the source.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered source domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'threat', - title: 'Threat', - group: 2, - description: - 'Fields to classify events and alerts according to a threat taxonomy\nsuch as the Mitre ATT&CK framework.\n\nThese fields are for users to classify alerts from all of their sources (e.g.\nIDS, NGFW, etc.) within a common taxonomy. The threat.tactic.* are meant to\ncapture the high level category of the threat (e.g. "impact"). The threat.technique.*\nfields are meant to capture which kind of approach is used by this detected\nthreat, to accomplish the goal (e.g. "endpoint denial of service").', - type: 'group', - fields: [ - { - name: 'framework', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the threat framework used to further categorize and classify\nthe tactic and technique of the reported threat. Framework classification\ncan be provided by detecting systems, evaluated at ingest time, or retrospectively\ntagged to events.', - example: 'MITRE ATT&CK', - }, - { - name: 'tactic.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The id of tactic used by this threat. You can use the Mitre ATT&CK\nMatrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/\n)', - example: 'TA0040', - }, - { - name: 'tactic.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the type of tactic used by this threat. You can use the\nMitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/\n)', - example: 'impact', - }, - { - name: 'tactic.reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The reference url of tactic used by this threat. You can use the\nMitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/\n)', - example: 'https://attack.mitre.org/tactics/TA0040/', - }, - { - name: 'technique.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The id of technique used by this tactic. You can use the Mitre\nATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/\n)', - example: 'T1499', - }, - { - name: 'technique.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'The name of technique used by this tactic. You can use the Mitre\nATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/\n)', - example: 'endpoint denial of service', - }, - { - name: 'technique.reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The reference url of technique used by this tactic. You can use\nthe Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/\n)', - example: 'https://attack.mitre.org/techniques/T1499/', - }, - ], - }, - { - name: 'tls', - title: 'TLS', - group: 2, - description: - 'Fields related to a TLS connection. These fields focus on the TLS\nprotocol itself and intentionally avoids in-depth analysis of the related x.509\ncertificate files.', - type: 'group', - fields: [ - { - name: 'cipher', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'String indicating the cipher used during the current connection.', - example: 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256', - default_field: false, - }, - { - name: 'client.certificate', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'PEM-encoded stand-alone certificate offered by the client. This\nis usually mutually-exclusive of `client.certificate_chain` since this value\nalso exists in that list.', - example: 'MII...', - default_field: false, - }, - { - name: 'client.certificate_chain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of PEM-encoded certificates that make up the certificate\nchain offered by the client. This is usually mutually-exclusive of `client.certificate`\nsince that value should be the first certificate in the chain.', - example: ['MII...', 'MII...'], - default_field: false, - }, - { - name: 'client.hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the MD5 digest of DER-encoded version\nof certificate offered by the client. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC', - default_field: false, - }, - { - name: 'client.hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA1 digest of DER-encoded version\nof certificate offered by the client. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '9E393D93138888D288266C2D915214D1D1CCEB2A', - default_field: false, - }, - { - name: 'client.hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA256 digest of DER-encoded\nversion of certificate offered by the client. For consistency with other hash\nvalues, this value should be formatted as an uppercase hash.', - example: '0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0', - default_field: false, - }, - { - name: 'client.issuer', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Distinguished name of subject of the issuer of the x.509 certificate\npresented by the client.', - example: 'CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'client.ja3', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash that identifies clients based on how they perform an SSL/TLS\nhandshake.', - example: 'd4e5b18d6b55c71272893221c96ba240', - default_field: false, - }, - { - name: 'client.not_after', - level: 'extended', - type: 'date', - description: - 'Date/Time indicating when client certificate is no longer considered\nvalid.', - example: '2021-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'client.not_before', - level: 'extended', - type: 'date', - description: 'Date/Time indicating when client certificate is first considered\nvalid.', - example: '1970-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'client.server_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Also called an SNI, this tells the server which hostname to which\nthe client is attempting to connect. When this value is available, it should\nget copied to `destination.domain`.', - example: 'www.elastic.co', - default_field: false, - }, - { - name: 'client.subject', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Distinguished name of subject of the x.509 certificate presented\nby the client.', - example: 'CN=myclient, OU=Documentation Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'client.supported_ciphers', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Array of ciphers offered by the client during the client hello.', - example: [ - 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384', - 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384', - '...', - ], - default_field: false, - }, - { - name: 'curve', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'String indicating the curve used for the given cipher, when applicable.', - example: 'secp256r1', - default_field: false, - }, - { - name: 'established', - level: 'extended', - type: 'boolean', - description: - 'Boolean flag indicating if the TLS negotiation was successful and\ntransitioned to an encrypted tunnel.', - default_field: false, - }, - { - name: 'next_protocol', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'String indicating the protocol being tunneled. Per the values in\nthe IANA registry (https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids),\nthis string should be lower case.', - example: 'http/1.1', - default_field: false, - }, - { - name: 'resumed', - level: 'extended', - type: 'boolean', - description: - 'Boolean flag indicating if this TLS connection was resumed from\nan existing TLS negotiation.', - default_field: false, - }, - { - name: 'server.certificate', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'PEM-encoded stand-alone certificate offered by the server. This\nis usually mutually-exclusive of `server.certificate_chain` since this value\nalso exists in that list.', - example: 'MII...', - default_field: false, - }, - { - name: 'server.certificate_chain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of PEM-encoded certificates that make up the certificate\nchain offered by the server. This is usually mutually-exclusive of `server.certificate`\nsince that value should be the first certificate in the chain.', - example: ['MII...', 'MII...'], - default_field: false, - }, - { - name: 'server.hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the MD5 digest of DER-encoded version\nof certificate offered by the server. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC', - default_field: false, - }, - { - name: 'server.hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA1 digest of DER-encoded version\nof certificate offered by the server. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '9E393D93138888D288266C2D915214D1D1CCEB2A', - default_field: false, - }, - { - name: 'server.hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA256 digest of DER-encoded\nversion of certificate offered by the server. For consistency with other hash\nvalues, this value should be formatted as an uppercase hash.', - example: '0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0', - default_field: false, - }, - { - name: 'server.issuer', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Subject of the issuer of the x.509 certificate presented by the\nserver.', - example: 'CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'server.ja3s', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash that identifies servers based on how they perform an SSL/TLS\nhandshake.', - example: '394441ab65754e2207b1e1b457b3641d', - default_field: false, - }, - { - name: 'server.not_after', - level: 'extended', - type: 'date', - description: - 'Timestamp indicating when server certificate is no longer considered\nvalid.', - example: '2021-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'server.not_before', - level: 'extended', - type: 'date', - description: 'Timestamp indicating when server certificate is first considered\nvalid.', - example: '1970-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'server.subject', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Subject of the x.509 certificate presented by the server.', - example: 'CN=www.mydomain.com, OU=Infrastructure Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Numeric part of the version parsed from the original string.', - example: '1.2', - default_field: false, - }, - { - name: 'version_protocol', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Normalized lowercase protocol name parsed from original string.', - example: 'tls', - default_field: false, - }, - ], - }, - { - name: 'tracing', - title: 'Tracing', - group: 2, - description: - 'Distributed tracing makes it possible to analyze performance throughout\na microservice architecture all in one view. This is accomplished by tracing\nall of the requests - from the initial web request in the front-end service\n- to queries made through multiple back-end services.', - type: 'group', - fields: [ - { - name: 'trace.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of the trace.\n\nA trace groups multiple events like transactions that belong together. For\nexample, a user request handled by multiple inter-connected services.', - example: '4bf92f3577b34da6a3ce929d0e0e4736', - }, - { - name: 'transaction.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of the transaction.\n\nA transaction is the highest level of work measured within a service, such\nas a request to a server.', - example: '00f067aa0ba902b7', - }, - ], - }, - { - name: 'url', - title: 'URL', - group: 2, - description: - 'URL fields provide support for complete or partial URLs, and supports\nthe breaking down into scheme, domain, path, and so on.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Domain of the url, such as "www.elastic.co".\n\nIn some cases a URL may refer to an IP and/or port directly, without a domain\nname. In this case, the IP address would go to the `domain` field.', - example: 'www.elastic.co', - }, - { - name: 'extension', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The field contains the file extension from the original request\nurl.\n\nThe file extension is only set if it exists, as not every url has a file extension.\n\nThe leading period must not be included. For example, the value must be "png",\nnot ".png".', - example: 'png', - }, - { - name: 'fragment', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Portion of the url after the `#`, such as "top".\n\nThe `#` is not part of the fragment.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'If full URLs are important to your use case, they should be stored\nin `url.full`, whether this field is reconstructed or present in the event\nsource.', - example: 'https://www.elastic.co:443/search?q=elasticsearch#top', - }, - { - name: 'original', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'Unmodified original url as seen in the event source.\n\nNote that in network monitoring, the observed URL may be a full URL, whereas\nin access logs, the URL is often just represented as a path.\n\nThis field is meant to represent the URL as it was observed, complete or not.', - example: - 'https://www.elastic.co:443/search?q=elasticsearch#top or /search?q=elasticsearch', - }, - { - name: 'password', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Password of the request.', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Path of the request, such as "/search".', - }, - { - name: 'port', - level: 'extended', - type: 'long', - format: 'string', - description: 'Port of the request, such as 443.', - example: 443, - }, - { - name: 'query', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The query field describes the query string of the request, such\nas "q=elasticsearch".\n\nThe `?` is excluded from the query string. If a URL contains no `?`, there\nis no query field. If there is a `?` but no query, the query field exists\nwith an empty string. The `exists` query can be used to differentiate between\nthe two cases.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered url domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'scheme', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Scheme of the request, such as "https".\n\nNote: The `:` is not part of the scheme.', - example: 'https', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'username', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Username of the request.', - }, - ], - }, - { - name: 'user', - title: 'User', - group: 2, - description: - 'The user fields describe information about the user that is relevant\nto the event.\n\nFields can have one entry or multiple entries. If a user has more than one id,\nprovide an array that includes all of them.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'user_agent', - title: 'User agent', - group: 2, - description: - 'The user_agent fields normally come from a browser request.\n\nThey often show up in web service logs coming from the parsed user agent string.', - type: 'group', - fields: [ - { - name: 'device.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the device.', - example: 'iPhone', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the user agent.', - example: 'Safari', - }, - { - name: 'original', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'Unparsed user_agent string.', - example: - 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15\n(KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Version of the user agent.', - example: 12, - }, - ], - }, - { - name: 'vlan', - title: 'VLAN', - group: 2, - description: - 'The VLAN fields are used to identify 802.1q tag(s) of a packet,\nas well as ingress and egress VLAN associations of an observer in relation to\na specific packet or connection.\n\nNetwork.vlan fields are used to record a single VLAN tag, or the outer tag in\nthe case of q-in-q encapsulations, for a packet or connection as observed, typically\nprovided by a network sensor (e.g. Zeek, Wireshark) passively reporting on traffic.\n\nNetwork.inner VLAN fields are used to report inner q-in-q 802.1q tags (multiple\n802.1q encapsulations) as observed, typically provided by a network sensor (e.g.\nZeek, Wireshark) passively reporting on traffic. Network.inner VLAN fields should\nonly be used in addition to network.vlan fields to indicate q-in-q tagging.\n\nObserver.ingress and observer.egress VLAN values are used to record observer\nspecific information when observer events contain discrete ingress and egress\nVLAN information, typically provided by firewalls, routers, or load balancers.', - type: 'group', - fields: [ - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - ], - }, - { - name: 'vulnerability', - title: 'Vulnerability', - group: 2, - description: - 'The vulnerability fields describe information about a vulnerability\nthat is relevant to an event.', - type: 'group', - fields: [ - { - name: 'category', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of system or architecture that the vulnerability affects.\nThese may be platform-specific (for example, Debian or SUSE) or general (for\nexample, Database or Firewall). For example (https://qualysguard.qualys.com/qwebhelp/fo_portal/knowledgebase/vulnerability_categories.htm[Qualys\nvulnerability categories])\n\nThis field must be an array.', - example: '["Firewall"]', - default_field: false, - }, - { - name: 'classification', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The classification of the vulnerability scoring system. For example\n(https://www.first.org/cvss/)', - example: 'CVSS', - default_field: false, - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'The description of the vulnerability that provides additional context\nof the vulnerability. For example (https://cve.mitre.org/about/faqs.html#cve_entry_descriptions_created[Common\nVulnerabilities and Exposure CVE description])', - example: 'In macOS before 2.12.6, there is a vulnerability in the RPC...', - default_field: false, - }, - { - name: 'enumeration', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of identifier used for this vulnerability. For example\n(https://cve.mitre.org/about/)', - example: 'CVE', - default_field: false, - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The identification (ID) is the number portion of a vulnerability\nentry. It includes a unique identification number for the vulnerability. For\nexample (https://cve.mitre.org/about/faqs.html#what_is_cve_id)[Common Vulnerabilities\nand Exposure CVE ID]', - example: 'CVE-2019-00001', - default_field: false, - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A resource that provides additional information, context, and mitigations\nfor the identified vulnerability.', - example: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6111', - default_field: false, - }, - { - name: 'report_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The report or scan identification number.', - example: 20191018.0001, - default_field: false, - }, - { - name: 'scanner.vendor', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The name of the vulnerability scanner vendor.', - example: 'Tenable', - default_field: false, - }, - { - name: 'score.base', - level: 'extended', - type: 'float', - description: - 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe.\n\nBase scores cover an assessment for exploitability metrics (attack vector,\ncomplexity, privileges, and user interaction), impact metrics (confidentiality,\nintegrity, and availability), and scope. For example (https://www.first.org/cvss/specification-document)', - example: 5.5, - default_field: false, - }, - { - name: 'score.environmental', - level: 'extended', - type: 'float', - description: - 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe.\n\nEnvironmental scores cover an assessment for any modified Base metrics, confidentiality,\nintegrity, and availability requirements. For example (https://www.first.org/cvss/specification-document)', - example: 5.5, - default_field: false, - }, - { - name: 'score.temporal', - level: 'extended', - type: 'float', - description: - 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe.\n\nTemporal scores cover an assessment for code maturity, remediation level,\nand confidence. For example (https://www.first.org/cvss/specification-document)', - default_field: false, - }, - { - name: 'score.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The National Vulnerability Database (NVD) provides qualitative\nseverity rankings of "Low", "Medium", and "High" for CVSS v2.0 base score\nranges in addition to the severity ratings for CVSS v3.0 as they are defined\nin the CVSS v3.0 specification.\n\nCVSS is owned and managed by FIRST.Org, Inc. (FIRST), a US-based non-profit\norganization, whose mission is to help computer security incident response\nteams across the world. For example (https://nvd.nist.gov/vuln-metrics/cvss)', - example: 2, - default_field: false, - }, - { - name: 'severity', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The severity of the vulnerability can help with metrics and internal\nprioritization regarding remediation. For example (https://nvd.nist.gov/vuln-metrics/cvss)', - example: 'Critical', - default_field: false, - }, - ], - }, - ], - }, - { - key: 'beat', - anchor: 'beat-common', - title: 'Beat', - description: 'Contains common beat fields available in all event types.\n', - fields: [ - { - name: 'agent.hostname', - type: 'keyword', - description: 'Hostname of the agent.', - }, - { - name: 'beat.timezone', - type: 'alias', - path: 'event.timezone', - migration: true, - }, - { - name: 'fields', - type: 'object', - object_type: 'keyword', - description: 'Contains user configurable fields.\n', - }, - { - name: 'beat.name', - type: 'alias', - path: 'host.name', - migration: true, - }, - { - name: 'beat.hostname', - type: 'alias', - path: 'agent.hostname', - migration: true, - }, - { - name: 'timeseries.instance', - type: 'keyword', - description: 'Time series instance id', - }, - ], - }, - { - key: 'cloud', - title: 'Cloud provider metadata', - description: 'Metadata from cloud providers added by the add_cloud_metadata processor.\n', - fields: [ - { - name: 'cloud.project.id', - example: 'project-x', - description: 'Name of the project in Google Cloud.\n', - }, - { - name: 'cloud.image.id', - example: 'ami-abcd1234', - description: 'Image ID for the cloud instance.\n', - }, - { - name: 'meta.cloud.provider', - type: 'alias', - path: 'cloud.provider', - migration: true, - }, - { - name: 'meta.cloud.instance_id', - type: 'alias', - path: 'cloud.instance.id', - migration: true, - }, - { - name: 'meta.cloud.instance_name', - type: 'alias', - path: 'cloud.instance.name', - migration: true, - }, - { - name: 'meta.cloud.machine_type', - type: 'alias', - path: 'cloud.machine.type', - migration: true, - }, - { - name: 'meta.cloud.availability_zone', - type: 'alias', - path: 'cloud.availability_zone', - migration: true, - }, - { - name: 'meta.cloud.project_id', - type: 'alias', - path: 'cloud.project.id', - migration: true, - }, - { - name: 'meta.cloud.region', - type: 'alias', - path: 'cloud.region', - migration: true, - }, - ], - }, - { - key: 'docker', - title: 'Docker', - description: 'Docker stats collected from Docker.\n', - short_config: false, - anchor: 'docker-processor', - fields: [ - { - name: 'docker', - type: 'group', - fields: [ - { - name: 'container.id', - type: 'alias', - path: 'container.id', - migration: true, - }, - { - name: 'container.image', - type: 'alias', - path: 'container.image.name', - migration: true, - }, - { - name: 'container.name', - type: 'alias', - path: 'container.name', - migration: true, - }, - { - name: 'container.labels', - type: 'object', - object_type: 'keyword', - description: 'Image labels.\n', - }, - ], - }, - ], - }, - { - key: 'host', - title: 'Host', - description: 'Info collected for the host machine.\n', - anchor: 'host-processor', - fields: [ - { - name: 'host', - type: 'group', - fields: [ - { - name: 'containerized', - type: 'boolean', - description: 'If the host is a container.\n', - }, - { - name: 'os.build', - type: 'keyword', - example: '18D109', - description: 'OS build information.\n', - }, - { - name: 'os.codename', - type: 'keyword', - example: 'stretch', - description: 'OS codename, if any.\n', - }, - ], - }, - ], - }, - { - key: 'kubernetes', - title: 'Kubernetes', - description: 'Kubernetes metadata added by the kubernetes processor\n', - short_config: false, - anchor: 'kubernetes-processor', - fields: [ - { - name: 'kubernetes', - type: 'group', - fields: [ - { - name: 'pod.name', - type: 'keyword', - description: 'Kubernetes pod name\n', - }, - { - name: 'pod.uid', - type: 'keyword', - description: 'Kubernetes Pod UID\n', - }, - { - name: 'namespace', - type: 'keyword', - description: 'Kubernetes namespace\n', - }, - { - name: 'node.name', - type: 'keyword', - description: 'Kubernetes node name\n', - }, - { - name: 'labels.*', - type: 'object', - object_type: 'keyword', - object_type_mapping_type: '*', - description: 'Kubernetes labels map\n', - }, - { - name: 'annotations.*', - type: 'object', - object_type: 'keyword', - object_type_mapping_type: '*', - description: 'Kubernetes annotations map\n', - }, - { - name: 'replicaset.name', - type: 'keyword', - description: 'Kubernetes replicaset name\n', - }, - { - name: 'deployment.name', - type: 'keyword', - description: 'Kubernetes deployment name\n', - }, - { - name: 'statefulset.name', - type: 'keyword', - description: 'Kubernetes statefulset name\n', - }, - { - name: 'container.name', - type: 'keyword', - description: 'Kubernetes container name\n', - }, - { - name: 'container.image', - type: 'keyword', - description: 'Kubernetes container image\n', - }, - ], - }, - ], - }, - { - key: 'process', - title: 'Process', - description: 'Process metadata fields\n', - fields: [ - { - name: 'process', - type: 'group', - fields: [ - { - name: 'exe', - type: 'alias', - path: 'process.executable', - migration: true, - }, - ], - }, - ], - }, - { - key: 'jolokia-autodiscover', - title: 'Jolokia Discovery autodiscover provider', - description: 'Metadata from Jolokia Discovery added by the jolokia provider.\n', - fields: [ - { - name: 'jolokia.agent.version', - type: 'keyword', - description: 'Version number of jolokia agent.\n', - }, - { - name: 'jolokia.agent.id', - type: 'keyword', - description: - 'Each agent has a unique id which can be either provided during startup of the agent in form of a configuration parameter or being autodetected. If autodected, the id has several parts: The IP, the process id, hashcode of the agent and its type.\n', - }, - { - name: 'jolokia.server.product', - type: 'keyword', - description: 'The container product if detected.\n', - }, - { - name: 'jolokia.server.version', - type: 'keyword', - description: "The container's version (if detected).\n", - }, - { - name: 'jolokia.server.vendor', - type: 'keyword', - description: 'The vendor of the container the agent is running in.\n', - }, - { - name: 'jolokia.url', - type: 'keyword', - description: 'The URL how this agent can be contacted.\n', - }, - { - name: 'jolokia.secured', - type: 'boolean', - description: 'Whether the agent was configured for authentication or not.\n', - }, - ], - }, - { - key: 'log', - title: 'Log file content', - description: 'Contains log file lines.\n', - fields: [ - { - name: 'log.file.path', - type: 'keyword', - required: false, - description: - 'The file from which the line was read. This field contains the absolute path to the file. For example: `/var/log/system.log`.\n', - }, - { - name: 'log.source.address', - type: 'keyword', - required: false, - description: 'Source address from which the log event was read / sent from.\n', - }, - { - name: 'log.offset', - type: 'long', - required: false, - description: 'The file offset the reported line starts at.\n', - }, - { - name: 'stream', - type: 'keyword', - required: false, - description: "Log stream when reading container logs, can be 'stdout' or 'stderr'\n", - }, - { - name: 'input.type', - required: true, - description: - 'The input type from which the event was generated. This field is set to the value specified for the `type` option in the input section of the Filebeat config file.\n', - }, - { - name: 'syslog.facility', - type: 'long', - required: false, - description: 'The facility extracted from the priority.\n', - }, - { - name: 'syslog.priority', - type: 'long', - required: false, - description: 'The priority of the syslog event.\n', - }, - { - name: 'syslog.severity_label', - type: 'keyword', - required: false, - description: 'The human readable severity.\n', - }, - { - name: 'syslog.facility_label', - type: 'keyword', - required: false, - description: 'The human readable facility.\n', - }, - { - name: 'process.program', - type: 'keyword', - required: false, - description: 'The name of the program.\n', - }, - { - name: 'log.flags', - description: 'This field contains the flags of the event.\n', - }, - { - name: 'http.response.content_length', - type: 'alias', - path: 'http.response.body.bytes', - migration: true, - }, - { - name: 'user_agent', - type: 'group', - fields: [ - { - name: 'os', - type: 'group', - fields: [ - { - name: 'full_name', - type: 'keyword', - }, - ], - }, - ], - }, - { - name: 'fileset.name', - type: 'keyword', - description: 'The Filebeat fileset that generated this event.\n', - }, - { - name: 'fileset.module', - type: 'alias', - path: 'event.module', - migration: true, - }, - { - name: 'read_timestamp', - type: 'alias', - path: 'event.created', - migration: true, - }, - { - name: 'docker.attrs', - type: 'object', - object_type: 'keyword', - description: - "docker.attrs contains labels and environment variables written by docker's JSON File logging driver. These fields are only available when they are configured in the logging driver options.\n", - }, - { - name: 'icmp.code', - type: 'keyword', - description: 'ICMP code.\n', - }, - { - name: 'icmp.type', - type: 'keyword', - description: 'ICMP type.\n', - }, - { - name: 'igmp.type', - type: 'keyword', - description: 'IGMP type.\n', - }, - { - name: 'azure', - type: 'group', - fields: [ - { - name: 'eventhub', - type: 'keyword', - description: 'Name of the eventhub.\n', - }, - { - name: 'offset', - type: 'long', - description: 'The offset.\n', - }, - { - name: 'enqueued_time', - type: 'date', - description: 'The enqueued time.\n', - }, - { - name: 'partition_id', - type: 'long', - description: 'The partition id.\n', - }, - { - name: 'consumer_group', - type: 'keyword', - description: 'The consumer group.\n', - }, - { - name: 'sequence_number', - type: 'long', - description: 'The sequence number.\n', - }, - ], - }, - { - name: 'kafka', - type: 'group', - fields: [ - { - name: 'topic', - type: 'keyword', - description: 'Kafka topic\n', - }, - { - name: 'partition', - type: 'long', - description: 'Kafka partition number\n', - }, - { - name: 'offset', - type: 'long', - description: 'Kafka offset of this message\n', - }, - { - name: 'key', - type: 'keyword', - description: 'Kafka key, corresponding to the Kafka value stored in the message\n', - }, - { - name: 'block_timestamp', - type: 'date', - description: 'Kafka outer (compressed) block timestamp\n', - }, - { - name: 'headers', - type: 'array', - description: - 'An array of Kafka header strings for this message, in the form ": ".\n', - }, - ], - }, - ], - }, - { - key: 'apache', - title: 'Apache', - description: 'Apache Module\n', - short_config: true, - fields: [ - { - name: 'apache2', - type: 'group', - description: 'Aliases for backward compatibility with old apache2 fields\n', - fields: [ - { - name: 'access', - type: 'group', - fields: [ - { - name: 'remote_ip', - type: 'alias', - path: 'source.address', - migration: true, - }, - { - name: 'ssl.protocol', - type: 'alias', - path: 'apache.access.ssl.protocol', - migration: true, - }, - { - name: 'ssl.cipher', - type: 'alias', - path: 'apache.access.ssl.cipher', - migration: true, - }, - { - name: 'body_sent.bytes', - type: 'alias', - path: 'http.response.body.bytes', - migration: true, - }, - { - name: 'user_name', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'method', - type: 'alias', - path: 'http.request.method', - migration: true, - }, - { - name: 'url', - type: 'alias', - path: 'url.original', - migration: true, - }, - { - name: 'http_version', - type: 'alias', - path: 'http.version', - migration: true, - }, - { - name: 'response_code', - type: 'alias', - path: 'http.response.status_code', - migration: true, - }, - { - name: 'referrer', - type: 'alias', - path: 'http.request.referrer', - migration: true, - }, - { - name: 'agent', - type: 'alias', - path: 'user_agent.original', - migration: true, - }, - { - name: 'user_agent', - type: 'group', - fields: [ - { - name: 'device', - type: 'alias', - path: 'user_agent.device.name', - migration: true, - }, - { - name: 'name', - type: 'alias', - path: 'user_agent.name', - migration: true, - }, - { - name: 'os', - type: 'alias', - path: 'user_agent.os.full_name', - migration: true, - }, - { - name: 'os_name', - type: 'alias', - path: 'user_agent.os.name', - migration: true, - }, - { - name: 'original', - type: 'alias', - path: 'user_agent.original', - migration: true, - }, - ], - }, - { - name: 'geoip', - type: 'group', - fields: [ - { - name: 'continent_name', - type: 'alias', - path: 'source.geo.continent_name', - migration: true, - }, - { - name: 'country_iso_code', - type: 'alias', - path: 'source.geo.country_iso_code', - migration: true, - }, - { - name: 'location', - type: 'alias', - path: 'source.geo.location', - migration: true, - }, - { - name: 'region_name', - type: 'alias', - path: 'source.geo.region_name', - migration: true, - }, - { - name: 'city_name', - type: 'alias', - path: 'source.geo.city_name', - migration: true, - }, - { - name: 'region_iso_code', - type: 'alias', - path: 'source.geo.region_iso_code', - migration: true, - }, - ], - }, - ], - }, - { - name: 'error', - type: 'group', - fields: [ - { - name: 'level', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - { - name: 'pid', - type: 'alias', - path: 'process.pid', - migration: true, - }, - { - name: 'tid', - type: 'alias', - path: 'process.thread.id', - migration: true, - }, - { - name: 'module', - type: 'alias', - path: 'apache.error.module', - migration: true, - }, - ], - }, - ], - }, - { - name: 'apache', - type: 'group', - description: 'Apache fields.\n', - fields: [ - { - name: 'access', - type: 'group', - description: 'Contains fields for the Apache HTTP Server access logs.\n', - fields: [ - { - name: 'ssl.protocol', - type: 'keyword', - description: 'SSL protocol version.\n', - }, - { - name: 'ssl.cipher', - type: 'keyword', - description: 'SSL cipher name.\n', - }, - ], - }, - { - name: 'error', - type: 'group', - description: 'Fields from the Apache error logs.\n', - fields: [ - { - name: 'module', - type: 'keyword', - description: 'The module producing the logged message.\n', - }, - ], - }, - ], - }, - ], - }, - { - key: 'auditd', - title: 'Auditd', - description: 'Module for parsing auditd logs.\n', - short_config: true, - fields: [ - { - name: 'user', - type: 'group', - fields: [ - { - name: 'terminal', - type: 'keyword', - description: - 'Terminal or tty device on which the user is performing the observed activity.\n', - }, - { - name: 'audit', - type: 'group', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.\n', - }, - { - name: 'name', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.\n', - }, - { - name: 'group.id', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.\n', - }, - { - name: 'group.name', - type: 'keyword', - description: 'Name of the group.\n', - }, - ], - }, - { - name: 'effective', - type: 'group', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.\n', - }, - { - name: 'name', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.\n', - }, - { - name: 'group.id', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.\n', - }, - { - name: 'group.name', - type: 'keyword', - description: 'Name of the group.\n', - }, - ], - }, - { - name: 'filesystem', - type: 'group', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.\n', - }, - { - name: 'name', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.\n', - }, - { - name: 'group.id', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.\n', - }, - { - name: 'group.name', - type: 'keyword', - description: 'Name of the group.\n', - }, - ], - }, - { - name: 'owner', - type: 'group', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.\n', - }, - { - name: 'name', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.\n', - }, - { - name: 'group.id', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.\n', - }, - { - name: 'group.name', - type: 'keyword', - description: 'Name of the group.\n', - }, - ], - }, - { - name: 'saved', - type: 'group', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.\n', - }, - { - name: 'name', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.\n', - }, - { - name: 'group.id', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.\n', - }, - { - name: 'group.name', - type: 'keyword', - description: 'Name of the group.\n', - }, - ], - }, - ], - }, - { - name: 'auditd', - type: 'group', - description: 'Fields from the auditd logs.\n', - fields: [ - { - name: 'log', - type: 'group', - description: - 'Fields from the Linux audit log. Not all fields are documented here because they are dynamic and vary by audit event type.\n', - fields: [ - { - name: 'old_auid', - description: - 'For login events this is the old audit ID used for the user prior to this login.\n', - }, - { - name: 'new_auid', - description: - 'For login events this is the new audit ID. The audit ID can be used to trace future events to the user even if their identity changes (like becoming root).\n', - }, - { - name: 'old_ses', - description: - 'For login events this is the old session ID used for the user prior to this login.\n', - }, - { - name: 'new_ses', - description: - 'For login events this is the new session ID. It can be used to tie a user to future events by session ID.\n', - }, - { - name: 'sequence', - type: 'long', - description: 'The audit event sequence number.\n', - }, - { - name: 'items', - description: 'The number of items in an event.\n', - }, - { - name: 'item', - description: - 'The item field indicates which item out of the total number of items. This number is zero-based; a value of 0 means it is the first item.\n', - }, - { - name: 'tty', - type: 'keyword', - definition: 'TTY udevice the user is running programs on.\n', - }, - { - name: 'a0', - description: 'The first argument to the system call.\n', - }, - { - name: 'addr', - type: 'ip', - definition: 'Remote address that the user is connecting from.\n', - }, - { - name: 'rport', - type: 'long', - definition: 'Remote port number.\n', - }, - { - name: 'laddr', - type: 'ip', - definition: 'Local network address.\n', - }, - { - name: 'lport', - type: 'long', - definition: 'Local port number.\n', - }, - { - name: 'acct', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'pid', - type: 'alias', - path: 'process.pid', - migration: true, - }, - { - name: 'ppid', - type: 'alias', - path: 'process.ppid', - migration: true, - }, - { - name: 'res', - type: 'alias', - path: 'event.outcome', - migration: true, - }, - { - name: 'record_type', - type: 'alias', - path: 'event.action', - migration: true, - }, - { - name: 'geoip', - type: 'group', - fields: [ - { - name: 'continent_name', - type: 'alias', - path: 'source.geo.continent_name', - migration: true, - }, - { - name: 'country_iso_code', - type: 'alias', - path: 'source.geo.country_iso_code', - migration: true, - }, - { - name: 'location', - type: 'alias', - path: 'source.geo.location', - migration: true, - }, - { - name: 'region_name', - type: 'alias', - path: 'source.geo.region_name', - migration: true, - }, - { - name: 'city_name', - type: 'alias', - path: 'source.geo.city_name', - migration: true, - }, - { - name: 'region_iso_code', - type: 'alias', - path: 'source.geo.region_iso_code', - migration: true, - }, - ], - }, - { - name: 'arch', - type: 'alias', - path: 'host.architecture', - migration: true, - }, - { - name: 'gid', - type: 'alias', - path: 'user.group.id', - migration: true, - }, - { - name: 'uid', - type: 'alias', - path: 'user.id', - migration: true, - }, - { - name: 'agid', - type: 'alias', - path: 'user.audit.group.id', - migration: true, - }, - { - name: 'auid', - type: 'alias', - path: 'user.audit.id', - migration: true, - }, - { - name: 'fsgid', - type: 'alias', - path: 'user.filesystem.group.id', - migration: true, - }, - { - name: 'fsuid', - type: 'alias', - path: 'user.filesystem.id', - migration: true, - }, - { - name: 'egid', - type: 'alias', - path: 'user.effective.group.id', - migration: true, - }, - { - name: 'euid', - type: 'alias', - path: 'user.effective.id', - migration: true, - }, - { - name: 'sgid', - type: 'alias', - path: 'user.saved.group.id', - migration: true, - }, - { - name: 'suid', - type: 'alias', - path: 'user.saved.id', - migration: true, - }, - { - name: 'ogid', - type: 'alias', - path: 'user.owner.group.id', - migration: true, - }, - { - name: 'ouid', - type: 'alias', - path: 'user.owner.id', - migration: true, - }, - { - name: 'comm', - type: 'alias', - path: 'process.name', - migration: true, - }, - { - name: 'exe', - type: 'alias', - path: 'process.executable', - migration: true, - }, - { - name: 'terminal', - type: 'alias', - path: 'user.terminal', - migration: true, - }, - { - name: 'msg', - type: 'alias', - path: 'message', - migration: true, - }, - { - name: 'src', - type: 'alias', - path: 'source.address', - migration: true, - }, - { - name: 'dst', - type: 'alias', - path: 'destination.address', - migration: true, - }, - ], - }, - ], - }, - ], - }, - { - key: 'elasticsearch', - title: 'elasticsearch', - description: 'elasticsearch Module\n', - fields: [ - { - name: 'elasticsearch', - type: 'group', - description: '\n', - fields: [ - { - name: 'component', - description: 'Elasticsearch component from where the log event originated', - example: 'o.e.c.m.MetaDataCreateIndexService', - type: 'keyword', - }, - { - name: 'cluster.uuid', - description: 'UUID of the cluster', - example: 'GmvrbHlNTiSVYiPf8kxg9g', - type: 'keyword', - }, - { - name: 'cluster.name', - description: 'Name of the cluster', - example: 'docker-cluster', - type: 'keyword', - }, - { - name: 'node.id', - description: 'ID of the node', - example: 'DSiWcTyeThWtUXLB9J0BMw', - type: 'keyword', - }, - { - name: 'node.name', - description: 'Name of the node', - example: 'vWNJsZ3', - type: 'keyword', - }, - { - name: 'index.name', - description: 'Index name', - example: 'filebeat-test-input', - type: 'keyword', - }, - { - name: 'index.id', - description: 'Index id', - example: 'aOGgDwbURfCV57AScqbCgw', - type: 'keyword', - }, - { - name: 'shard.id', - description: 'Id of the shard', - example: '0', - type: 'keyword', - }, - { - name: 'audit', - type: 'group', - description: '\n', - fields: [ - { - name: 'layer', - description: - 'The layer from which this event originated: rest, transport or ip_filter', - example: 'rest', - type: 'keyword', - }, - { - name: 'event_type', - description: - 'The type of event that occurred: anonymous_access_denied, authentication_failed, access_denied, access_granted, connection_granted, connection_denied, tampered_request, run_as_granted, run_as_denied', - example: 'access_granted', - type: 'keyword', - }, - { - name: 'origin.type', - description: - 'Where the request originated: rest (request originated from a REST API request), transport (request was received on the transport channel), local_node (the local node issued the request)', - example: 'local_node', - type: 'keyword', - }, - { - name: 'realm', - description: 'The authentication realm the authentication was validated against', - example: 'default_file', - type: 'keyword', - }, - { - name: 'user.realm', - description: "The user's authentication realm, if authenticated", - example: 'active_directory', - type: 'keyword', - }, - { - name: 'user.roles', - description: 'Roles to which the principal belongs', - example: ['kibana_user', 'beats_admin'], - type: 'keyword', - }, - { - name: 'action', - description: 'The name of the action that was executed', - example: 'cluster:monitor/main', - type: 'keyword', - }, - { - name: 'url.params', - description: 'REST URI parameters', - example: '{username=jacknich2}', - }, - { - name: 'indices', - description: 'Indices accessed by action', - example: ['foo-2019.01.04', 'foo-2019.01.03', 'foo-2019.01.06'], - type: 'keyword', - }, - { - name: 'request.id', - description: 'Unique ID of request', - example: 'WzL_kb6VSvOhAq0twPvHOQ', - type: 'keyword', - }, - { - name: 'request.name', - description: 'The type of request that was executed', - example: 'ClearScrollRequest', - type: 'keyword', - }, - { - name: 'request_body', - type: 'alias', - path: 'http.request.body.content', - migration: true, - }, - { - name: 'origin_address', - type: 'alias', - path: 'source.ip', - migration: true, - }, - { - name: 'uri', - type: 'alias', - path: 'url.original', - migration: true, - }, - { - name: 'principal', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'message', - type: 'text', - }, - ], - }, - { - name: 'gc', - type: 'group', - description: 'GC fileset fields.\n', - fields: [ - { - name: 'phase', - type: 'group', - description: 'Fields specific to GC phase.\n', - fields: [ - { - name: 'name', - type: 'keyword', - description: 'Name of the GC collection phase.\n', - }, - { - name: 'duration_sec', - type: 'float', - description: - 'Collection phase duration according to the Java virtual machine.\n', - }, - { - name: 'scrub_symbol_table_time_sec', - type: 'float', - description: 'Pause time in seconds cleaning up symbol tables.\n', - }, - { - name: 'scrub_string_table_time_sec', - type: 'float', - description: 'Pause time in seconds cleaning up string tables.\n', - }, - { - name: 'weak_refs_processing_time_sec', - type: 'float', - description: 'Time spent processing weak references in seconds.\n', - }, - { - name: 'parallel_rescan_time_sec', - type: 'float', - description: - 'Time spent in seconds marking live objects while application is stopped.\n', - }, - { - name: 'class_unload_time_sec', - type: 'float', - description: 'Time spent unloading unused classes in seconds.\n', - }, - { - name: 'cpu_time', - type: 'group', - description: 'Process CPU time spent performing collections.\n', - fields: [ - { - name: 'user_sec', - type: 'float', - description: 'CPU time spent outside the kernel.\n', - }, - { - name: 'sys_sec', - type: 'float', - description: 'CPU time spent inside the kernel.\n', - }, - { - name: 'real_sec', - type: 'float', - description: - 'Total elapsed CPU time spent to complete the collection from start to finish.\n', - }, - ], - }, - ], - }, - { - name: 'jvm_runtime_sec', - type: 'float', - description: 'The time from JVM start up in seconds, as a floating point number.\n', - }, - { - name: 'threads_total_stop_time_sec', - type: 'float', - description: 'Garbage collection threads total stop time seconds.\n', - }, - { - name: 'stopping_threads_time_sec', - type: 'float', - description: 'Time took to stop threads seconds.\n', - }, - { - name: 'tags', - type: 'keyword', - description: 'GC logging tags.\n', - }, - { - name: 'heap', - type: 'group', - description: 'Heap allocation and total size.\n', - fields: [ - { - name: 'size_kb', - type: 'integer', - description: 'Total heap size in kilobytes.\n', - }, - { - name: 'used_kb', - type: 'integer', - description: 'Used heap in kilobytes.\n', - }, - ], - }, - { - name: 'old_gen', - type: 'group', - description: 'Old generation occupancy and total size.\n', - fields: [ - { - name: 'size_kb', - type: 'integer', - description: 'Total size of old generation in kilobytes.\n', - }, - { - name: 'used_kb', - type: 'integer', - description: 'Old generation occupancy in kilobytes.\n', - }, - ], - }, - { - name: 'young_gen', - type: 'group', - description: 'Young generation occupancy and total size.\n', - fields: [ - { - name: 'size_kb', - type: 'integer', - description: 'Total size of young generation in kilobytes.\n', - }, - { - name: 'used_kb', - type: 'integer', - description: 'Young generation occupancy in kilobytes.\n', - }, - ], - }, - ], - }, - { - name: 'server', - description: 'Server log file', - type: 'group', - fields: [ - { - name: 'stacktrace', - description: 'Stack trace in case of errors', - index: false, - }, - { - name: 'gc', - description: 'GC log', - type: 'group', - fields: [ - { - name: 'young', - description: 'Young GC', - example: '', - type: 'group', - fields: [ - { - name: 'one', - description: '', - example: '', - type: 'long', - }, - { - name: 'two', - description: '', - example: '', - type: 'long', - }, - ], - }, - { - name: 'overhead_seq', - description: 'Sequence number', - example: 3449992, - type: 'long', - }, - { - name: 'collection_duration.ms', - description: 'Time spent in GC, in milliseconds', - example: 1600, - type: 'float', - }, - { - name: 'observation_duration.ms', - description: 'Total time over which collection was observed, in milliseconds', - example: 1800, - type: 'float', - }, - ], - }, - ], - }, - { - name: 'slowlog', - description: 'Slowlog events from Elasticsearch', - example: - '[2018-06-29T10:06:14,933][INFO ][index.search.slowlog.query] [v_VJhjV] [metricbeat-6.3.0-2018.06.26][0] took[4.5ms], took_millis[4], total_hits[19435], types[], stats[], search_type[QUERY_THEN_FETCH], total_shards[1], source[{"query":{"match_all":{"boost":1.0}}}],', - type: 'group', - fields: [ - { - name: 'logger', - description: 'Logger name', - example: 'index.search.slowlog.fetch', - type: 'keyword', - }, - { - name: 'took', - description: 'Time it took to execute the query', - example: '300ms', - type: 'keyword', - }, - { - name: 'types', - description: 'Types', - example: '', - type: 'keyword', - }, - { - name: 'stats', - description: 'Stats groups', - example: 'group1', - type: 'keyword', - }, - { - name: 'search_type', - description: 'Search type', - example: 'QUERY_THEN_FETCH', - type: 'keyword', - }, - { - name: 'source_query', - description: 'Slow query', - example: '{"query":{"match_all":{"boost":1.0}}}', - type: 'keyword', - }, - { - name: 'extra_source', - description: 'Extra source information', - example: '', - type: 'keyword', - }, - { - name: 'total_hits', - description: 'Total hits', - example: 42, - type: 'keyword', - }, - { - name: 'total_shards', - description: 'Total queried shards', - example: 22, - type: 'keyword', - }, - { - name: 'routing', - description: 'Routing', - example: 's01HZ2QBk9jw4gtgaFtn', - type: 'keyword', - }, - { - name: 'id', - description: 'Id', - example: '', - type: 'keyword', - }, - { - name: 'type', - description: 'Type', - example: 'doc', - type: 'keyword', - }, - { - name: 'source', - description: 'Source of document that was indexed', - type: 'keyword', - }, - ], - }, - ], - }, - ], - }, - { - key: 'haproxy', - title: 'haproxy', - description: 'haproxy Module\n', - fields: [ - { - name: 'haproxy', - type: 'group', - description: '\n', - fields: [ - { - name: 'frontend_name', - description: - 'Name of the frontend (or listener) which received and processed the connection.', - }, - { - name: 'backend_name', - description: - 'Name of the backend (or listener) which was selected to manage the connection to the server.', - }, - { - name: 'server_name', - description: 'Name of the last server to which the connection was sent.', - }, - { - name: 'total_waiting_time_ms', - description: 'Total time in milliseconds spent waiting in the various queues', - type: 'long', - }, - { - name: 'connection_wait_time_ms', - description: - 'Total time in milliseconds spent waiting for the connection to establish to the final server', - type: 'long', - }, - { - name: 'bytes_read', - description: 'Total number of bytes transmitted to the client when the log is emitted.', - type: 'long', - }, - { - name: 'time_queue', - description: 'Total time in milliseconds spent waiting in the various queues.', - type: 'long', - }, - { - name: 'time_backend_connect', - description: - 'Total time in milliseconds spent waiting for the connection to establish to the final server, including retries.', - type: 'long', - }, - { - name: 'server_queue', - description: - 'Total number of requests which were processed before this one in the server queue.', - type: 'long', - }, - { - name: 'backend_queue', - description: - "Total number of requests which were processed before this one in the backend's global queue.", - type: 'long', - }, - { - name: 'bind_name', - description: 'Name of the listening address which received the connection.', - }, - { - name: 'error_message', - description: 'Error message logged by HAProxy in case of error.', - type: 'text', - }, - { - name: 'source', - type: 'keyword', - description: 'The HAProxy source of the log', - }, - { - name: 'termination_state', - description: 'Condition the session was in when the session ended.', - }, - { - name: 'mode', - type: 'keyword', - description: 'mode that the frontend is operating (TCP or HTTP)', - }, - { - name: 'connections', - description: 'Contains various counts of connections active in the process.', - type: 'group', - fields: [ - { - name: 'active', - description: - 'Total number of concurrent connections on the process when the session was logged.', - type: 'long', - }, - { - name: 'frontend', - description: - 'Total number of concurrent connections on the frontend when the session was logged.', - type: 'long', - }, - { - name: 'backend', - description: - 'Total number of concurrent connections handled by the backend when the session was logged.', - type: 'long', - }, - { - name: 'server', - description: - 'Total number of concurrent connections still active on the server when the session was logged.', - type: 'long', - }, - { - name: 'retries', - description: - 'Number of connection retries experienced by this session when trying to connect to the server.', - type: 'long', - }, - ], - }, - { - name: 'client', - description: 'Information about the client doing the request', - type: 'group', - fields: [ - { - name: 'ip', - type: 'alias', - path: 'source.address', - migration: true, - }, - { - name: 'port', - type: 'alias', - path: 'source.port', - migration: true, - }, - ], - }, - { - name: 'process_name', - type: 'alias', - path: 'process.name', - migration: true, - }, - { - name: 'pid', - type: 'alias', - path: 'process.pid', - migration: true, - }, - { - name: 'destination', - description: 'Destination information', - type: 'group', - fields: [ - { - name: 'port', - type: 'alias', - path: 'destination.port', - migration: true, - }, - { - name: 'ip', - type: 'alias', - path: 'destination.ip', - migration: true, - }, - ], - }, - { - name: 'geoip', - type: 'group', - description: - 'Contains GeoIP information gathered based on the client.ip field. Only present if the GeoIP Elasticsearch plugin is available and used.\n', - fields: [ - { - name: 'continent_name', - type: 'alias', - path: 'source.geo.continent_name', - migration: true, - }, - { - name: 'country_iso_code', - type: 'alias', - path: 'source.geo.country_iso_code', - migration: true, - }, - { - name: 'location', - type: 'alias', - path: 'source.geo.location', - migration: true, - }, - { - name: 'region_name', - type: 'alias', - path: 'source.geo.region_name', - migration: true, - }, - { - name: 'city_name', - type: 'alias', - path: 'source.geo.city_name', - migration: true, - }, - { - name: 'region_iso_code', - type: 'alias', - path: 'source.geo.region_iso_code', - migration: true, - }, - ], - }, - { - name: 'http', - description: 'Please add description', - type: 'group', - fields: [ - { - name: 'response', - description: 'Fields related to the HTTP response', - type: 'group', - fields: [ - { - name: 'captured_cookie', - description: - 'Optional "name=value" entry indicating that the client had this cookie in the response.\n', - }, - { - name: 'captured_headers', - description: - 'List of headers captured in the response due to the presence of the "capture response header" statement in the frontend.\n', - type: 'keyword', - }, - { - name: 'status_code', - type: 'alias', - path: 'http.response.status_code', - migration: true, - }, - ], - }, - { - name: 'request', - description: 'Fields related to the HTTP request', - type: 'group', - fields: [ - { - name: 'captured_cookie', - description: - 'Optional "name=value" entry indicating that the server has returned a cookie with its request.\n', - }, - { - name: 'captured_headers', - description: - 'List of headers captured in the request due to the presence of the "capture request header" statement in the frontend.\n', - type: 'keyword', - }, - { - name: 'raw_request_line', - description: - 'Complete HTTP request line, including the method, request and HTTP version string.', - type: 'keyword', - }, - { - name: 'time_wait_without_data_ms', - description: - 'Total time in milliseconds spent waiting for the server to send a full HTTP response, not counting data.', - type: 'long', - }, - { - name: 'time_wait_ms', - description: - 'Total time in milliseconds spent waiting for a full HTTP request from the client (not counting body) after the first byte was received.', - type: 'long', - }, - ], - }, - ], - }, - { - name: 'tcp', - description: 'TCP log format', - type: 'group', - fields: [ - { - name: 'connection_waiting_time_ms', - type: 'long', - description: - 'Total time in milliseconds elapsed between the accept and the last close', - }, - ], - }, - ], - }, - ], - }, - { - key: 'icinga', - title: 'Icinga', - description: 'Icinga Module\n', - fields: [ - { - name: 'icinga', - type: 'group', - description: '\n', - fields: [ - { - name: 'debug', - type: 'group', - description: 'Contains fields for the Icinga debug logs.\n', - fields: [ - { - name: 'facility', - type: 'keyword', - description: 'Specifies what component of Icinga logged the message.\n', - }, - { - name: 'severity', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - ], - }, - { - name: 'main', - type: 'group', - description: 'Contains fields for the Icinga main logs.\n', - fields: [ - { - name: 'facility', - type: 'keyword', - description: 'Specifies what component of Icinga logged the message.\n', - }, - { - name: 'severity', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - ], - }, - { - name: 'startup', - type: 'group', - description: 'Contains fields for the Icinga startup logs.\n', - fields: [ - { - name: 'facility', - type: 'keyword', - description: 'Specifies what component of Icinga logged the message.\n', - }, - { - name: 'severity', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - ], - }, - ], - }, - ], - }, - { - key: 'iis', - title: 'IIS', - description: 'Module for parsing IIS log files.\n', - fields: [ - { - name: 'iis', - type: 'group', - description: 'Fields from IIS log files.\n', - fields: [ - { - name: 'access', - type: 'group', - description: 'Contains fields for IIS access logs.\n', - fields: [ - { - name: 'sub_status', - type: 'long', - description: 'The HTTP substatus code.\n', - }, - { - name: 'win32_status', - type: 'long', - description: 'The Windows status code.\n', - }, - { - name: 'site_name', - type: 'keyword', - description: 'The site name and instance number.\n', - }, - { - name: 'server_name', - type: 'keyword', - description: 'The name of the server on which the log file entry was generated.\n', - }, - { - name: 'cookie', - type: 'keyword', - description: 'The content of the cookie sent or received, if any.\n', - }, - { - name: 'body_received.bytes', - type: 'alias', - path: 'http.request.body.bytes', - migration: true, - }, - { - name: 'body_sent.bytes', - type: 'alias', - path: 'http.response.body.bytes', - migration: true, - }, - { - name: 'server_ip', - type: 'alias', - path: 'destination.address', - migration: true, - }, - { - name: 'method', - type: 'alias', - path: 'http.request.method', - migration: true, - }, - { - name: 'url', - type: 'alias', - path: 'url.path', - migration: true, - }, - { - name: 'query_string', - type: 'alias', - path: 'url.query', - migration: true, - }, - { - name: 'port', - type: 'alias', - path: 'destination.port', - migration: true, - }, - { - name: 'user_name', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'remote_ip', - type: 'alias', - path: 'source.address', - migration: true, - }, - { - name: 'referrer', - type: 'alias', - path: 'http.request.referrer', - migration: true, - }, - { - name: 'response_code', - type: 'alias', - path: 'http.response.status_code', - migration: true, - }, - { - name: 'http_version', - type: 'alias', - path: 'http.version', - migration: true, - }, - { - name: 'hostname', - type: 'alias', - path: 'host.hostname', - migration: true, - }, - { - name: 'user_agent', - type: 'group', - fields: [ - { - name: 'device', - type: 'alias', - path: 'user_agent.device.name', - migration: true, - }, - { - name: 'name', - type: 'alias', - path: 'user_agent.name', - migration: true, - }, - { - name: 'os', - type: 'alias', - path: 'user_agent.os.full_name', - migration: true, - }, - { - name: 'os_name', - type: 'alias', - path: 'user_agent.os.name', - migration: true, - }, - { - name: 'original', - type: 'alias', - path: 'user_agent.original', - migration: true, - }, - ], - }, - { - name: 'geoip', - type: 'group', - fields: [ - { - name: 'continent_name', - type: 'alias', - path: 'source.geo.continent_name', - migration: true, - }, - { - name: 'country_iso_code', - type: 'alias', - path: 'source.geo.country_iso_code', - migration: true, - }, - { - name: 'location', - type: 'alias', - path: 'source.geo.location', - migration: true, - }, - { - name: 'region_name', - type: 'alias', - path: 'source.geo.region_name', - migration: true, - }, - { - name: 'city_name', - type: 'alias', - path: 'source.geo.city_name', - migration: true, - }, - { - name: 'region_iso_code', - type: 'alias', - path: 'source.geo.region_iso_code', - migration: true, - }, - ], - }, - ], - }, - { - name: 'error', - type: 'group', - description: 'Contains fields for IIS error logs.\n', - fields: [ - { - name: 'reason_phrase', - type: 'keyword', - description: 'The HTTP reason phrase.\n', - }, - { - name: 'queue_name', - type: 'keyword', - description: 'The IIS application pool name.\n', - }, - { - name: 'remote_ip', - type: 'alias', - path: 'source.address', - migration: true, - }, - { - name: 'remote_port', - type: 'alias', - path: 'source.port', - migration: true, - }, - { - name: 'server_ip', - type: 'alias', - path: 'destination.address', - migration: true, - }, - { - name: 'server_port', - type: 'alias', - path: 'destination.port', - migration: true, - }, - { - name: 'http_version', - type: 'alias', - path: 'http.version', - migration: true, - }, - { - name: 'method', - type: 'alias', - path: 'http.request.method', - migration: true, - }, - { - name: 'url', - type: 'alias', - path: 'url.original', - migration: true, - }, - { - name: 'response_code', - type: 'alias', - path: 'http.response.status_code', - migration: true, - }, - { - name: 'geoip', - type: 'group', - fields: [ - { - name: 'continent_name', - type: 'alias', - path: 'source.geo.continent_name', - migration: true, - }, - { - name: 'country_iso_code', - type: 'alias', - path: 'source.geo.country_iso_code', - migration: true, - }, - { - name: 'location', - type: 'alias', - path: 'source.geo.location', - migration: true, - }, - { - name: 'region_name', - type: 'alias', - path: 'source.geo.region_name', - migration: true, - }, - { - name: 'city_name', - type: 'alias', - path: 'source.geo.city_name', - migration: true, - }, - { - name: 'region_iso_code', - type: 'alias', - path: 'source.geo.region_iso_code', - migration: true, - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - key: 'kafka', - title: 'Kafka', - description: 'Kafka module\n', - fields: [ - { - name: 'kafka', - type: 'group', - description: '\n', - fields: [ - { - name: 'log', - type: 'group', - description: 'Kafka log lines.\n', - fields: [ - { - name: 'level', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - { - name: 'component', - type: 'keyword', - description: 'Component the log is coming from.\n', - }, - { - name: 'class', - type: 'keyword', - description: 'Java class the log is coming from.\n', - }, - { - name: 'trace', - type: 'group', - description: 'Trace in the log line.\n', - fields: [ - { - name: 'class', - type: 'keyword', - description: 'Java class the trace is coming from.\n', - }, - { - name: 'message', - type: 'text', - description: 'Message part of the trace.\n', - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - key: 'kibana', - title: 'kibana', - description: 'kibana Module\n', - fields: [ - { - name: 'kibana', - type: 'group', - description: '\n', - fields: [ - { - name: 'log', - type: 'group', - description: 'Kafka log lines.\n', - fields: [ - { - name: 'tags', - type: 'keyword', - description: 'Kibana logging tags.\n', - }, - { - name: 'state', - type: 'keyword', - description: 'Current state of Kibana.\n', - }, - { - name: 'meta', - type: 'object', - object_type: 'keyword', - }, - { - name: 'kibana.log.meta.req.headers.referer', - type: 'alias', - path: 'http.request.referrer', - migration: true, - }, - { - name: 'kibana.log.meta.req.referer', - type: 'alias', - path: 'http.request.referrer', - migration: true, - }, - { - name: 'kibana.log.meta.req.headers.user-agent', - type: 'alias', - path: 'user_agent.original', - migration: true, - }, - { - name: 'kibana.log.meta.req.remoteAddress', - type: 'alias', - path: 'source.address', - migration: true, - }, - { - name: 'kibana.log.meta.req.url', - type: 'alias', - path: 'url.original', - migration: true, - }, - { - name: 'kibana.log.meta.statusCode', - type: 'alias', - path: 'http.response.status_code', - migration: true, - }, - { - name: 'kibana.log.meta.method', - type: 'alias', - path: 'http.request.method', - migration: true, - }, - ], - }, - ], - }, - ], - }, - { - key: 'logstash', - title: 'logstash', - description: 'logstash Module\n', - fields: [ - { - name: 'logstash', - type: 'group', - description: '\n', - fields: [ - { - name: 'log', - title: 'Logstash', - type: 'group', - description: 'Fields from the Logstash logs.\n', - fields: [ - { - name: 'module', - type: 'keyword', - description: 'The module or class where the event originate.\n', - }, - { - name: 'thread', - type: 'keyword', - description: 'Information about the running thread where the log originate.\n', - multi_fields: [ - { - name: 'text', - type: 'text', - }, - ], - }, - { - name: 'log_event', - type: 'object', - description: 'key and value debugging information.\n', - }, - { - name: 'pipeline_id', - type: 'keyword', - example: 'main', - description: 'The ID of the pipeline.\n', - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - { - name: 'level', - type: 'alias', - path: 'log.level', - migration: true, - }, - ], - }, - { - name: 'slowlog', - type: 'group', - description: 'slowlog\n', - fields: [ - { - name: 'module', - type: 'keyword', - description: 'The module or class where the event originate.\n', - }, - { - name: 'thread', - type: 'keyword', - description: 'Information about the running thread where the log originate.\n', - multi_fields: [ - { - name: 'text', - type: 'text', - }, - ], - }, - { - name: 'event', - type: 'keyword', - description: 'Raw dump of the original event\n', - multi_fields: [ - { - name: 'text', - type: 'text', - }, - ], - }, - { - name: 'plugin_name', - type: 'keyword', - description: 'Name of the plugin\n', - }, - { - name: 'plugin_type', - type: 'keyword', - description: 'Type of the plugin: Inputs, Filters, Outputs or Codecs.\n', - }, - { - name: 'took_in_millis', - type: 'long', - description: 'Execution time for the plugin in milliseconds.\n', - }, - { - name: 'plugin_params', - type: 'keyword', - description: 'String value of the plugin configuration\n', - multi_fields: [ - { - name: 'text', - type: 'text', - }, - ], - }, - { - name: 'plugin_params_object', - type: 'object', - description: 'key -> value of the configuration used by the plugin.\n', - }, - { - name: 'level', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'took_in_nanos', - type: 'alias', - path: 'event.duration', - migration: true, - }, - ], - }, - ], - }, - ], - }, - { - key: 'mongodb', - title: 'mongodb', - description: 'Module for parsing MongoDB log files.\n', - fields: [ - { - name: 'mongodb', - type: 'group', - description: 'Fields from MongoDB logs.\n', - fields: [ - { - name: 'log', - type: 'group', - description: 'Contains fields from MongoDB logs.\n', - fields: [ - { - name: 'component', - description: 'Functional categorization of message\n', - example: 'COMMAND', - type: 'keyword', - }, - { - name: 'context', - description: 'Context of message\n', - example: 'initandlisten', - type: 'keyword', - }, - { - name: 'severity', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - ], - }, - ], - }, - ], - }, - { - key: 'mysql', - title: 'MySQL', - description: 'Module for parsing the MySQL log files.\n', - short_config: true, - fields: [ - { - name: 'mysql', - type: 'group', - description: 'Fields from the MySQL log files.\n', - fields: [ - { - name: 'thread_id', - type: 'long', - description: 'The connection or thread ID for the query.\n', - }, - { - name: 'error', - type: 'group', - description: 'Contains fields from the MySQL error logs.\n', - fields: [ - { - name: 'thread_id', - type: 'alias', - path: 'mysql.thread_id', - migration: true, - }, - { - name: 'level', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - ], - }, - { - name: 'slowlog', - type: 'group', - description: 'Contains fields from the MySQL slow logs.\n', - fields: [ - { - name: 'lock_time.sec', - type: 'float', - description: - 'The amount of time the query waited for the lock to be available. The value is in seconds, as a floating point number.\n', - }, - { - name: 'rows_sent', - type: 'long', - description: 'The number of rows returned by the query.\n', - }, - { - name: 'rows_examined', - type: 'long', - description: 'The number of rows scanned by the query.\n', - }, - { - name: 'rows_affected', - type: 'long', - description: 'The number of rows modified by the query.\n', - }, - { - name: 'bytes_sent', - type: 'long', - format: 'bytes', - description: 'The number of bytes sent to client.\n', - }, - { - name: 'bytes_received', - type: 'long', - format: 'bytes', - description: 'The number of bytes received from client.\n', - }, - { - name: 'query', - description: 'The slow query.\n', - }, - { - name: 'id', - type: 'alias', - path: 'mysql.thread_id', - migration: true, - }, - { - name: 'schema', - type: 'keyword', - description: 'The schema where the slow query was executed.\n', - }, - { - name: 'current_user', - type: 'keyword', - description: - 'Current authenticated user, used to determine access privileges. Can differ from the value for user.\n', - }, - { - name: 'last_errno', - type: 'keyword', - description: 'Last SQL error seen.\n', - }, - { - name: 'killed', - type: 'keyword', - description: 'Code of the reason if the query was killed.\n', - }, - { - name: 'query_cache_hit', - type: 'boolean', - description: 'Whether the query cache was hit.\n', - }, - { - name: 'tmp_table', - type: 'boolean', - description: 'Whether a temporary table was used to resolve the query.\n', - }, - { - name: 'tmp_table_on_disk', - type: 'boolean', - description: 'Whether the query needed temporary tables on disk.\n', - }, - { - name: 'tmp_tables', - type: 'long', - description: 'Number of temporary tables created for this query\n', - }, - { - name: 'tmp_disk_tables', - type: 'long', - description: 'Number of temporary tables created on disk for this query.\n', - }, - { - name: 'tmp_table_sizes', - type: 'long', - format: 'bytes', - description: 'Size of temporary tables created for this query.', - }, - { - name: 'filesort', - type: 'boolean', - description: 'Whether filesort optimization was used.\n', - }, - { - name: 'filesort_on_disk', - type: 'boolean', - description: - 'Whether filesort optimization was used and it needed temporary tables on disk.\n', - }, - { - name: 'priority_queue', - type: 'boolean', - description: 'Whether a priority queue was used for filesort.\n', - }, - { - name: 'full_scan', - type: 'boolean', - description: 'Whether a full table scan was needed for the slow query.\n', - }, - { - name: 'full_join', - type: 'boolean', - description: - 'Whether a full join was needed for the slow query (no indexes were used for joins).\n', - }, - { - name: 'merge_passes', - type: 'long', - description: 'Number of merge passes executed for the query.\n', - }, - { - name: 'sort_merge_passes', - type: 'long', - description: 'Number of merge passes that the sort algorithm has had to do.\n', - }, - { - name: 'sort_range_count', - type: 'long', - description: 'Number of sorts that were done using ranges.\n', - }, - { - name: 'sort_rows', - type: 'long', - description: 'Number of sorted rows.\n', - }, - { - name: 'sort_scan_count', - type: 'long', - description: 'Number of sorts that were done by scanning the table.\n', - }, - { - name: 'log_slow_rate_type', - type: 'keyword', - description: - 'Type of slow log rate limit, it can be `session` if the rate limit is applied per session, or `query` if it applies per query.\n', - }, - { - name: 'log_slow_rate_limit', - type: 'keyword', - description: - 'Slow log rate limit, a value of 100 means that one in a hundred queries or sessions are being logged.\n', - }, - { - name: 'read_first', - type: 'long', - description: 'The number of times the first entry in an index was read.\n', - }, - { - name: 'read_last', - type: 'long', - description: 'The number of times the last key in an index was read.\n', - }, - { - name: 'read_key', - type: 'long', - description: 'The number of requests to read a row based on a key.\n', - }, - { - name: 'read_next', - type: 'long', - description: 'The number of requests to read the next row in key order.\n', - }, - { - name: 'read_prev', - type: 'long', - description: 'The number of requests to read the previous row in key order.\n', - }, - { - name: 'read_rnd', - type: 'long', - description: 'The number of requests to read a row based on a fixed position.\n', - }, - { - name: 'read_rnd_next', - type: 'long', - description: 'The number of requests to read the next row in the data file.\n', - }, - { - name: 'innodb', - type: 'group', - description: 'Contains fields relative to InnoDB engine\n', - fields: [ - { - name: 'trx_id', - type: 'keyword', - description: 'Transaction ID\n', - }, - { - name: 'io_r_ops', - type: 'long', - description: 'Number of page read operations.\n', - }, - { - name: 'io_r_bytes', - type: 'long', - format: 'bytes', - description: 'Bytes read during page read operations.\n', - }, - { - name: 'io_r_wait.sec', - type: 'long', - description: 'How long it took to read all needed data from storage.\n', - }, - { - name: 'rec_lock_wait.sec', - type: 'long', - description: 'How long the query waited for locks.\n', - }, - { - name: 'queue_wait.sec', - type: 'long', - description: - 'How long the query waited to enter the InnoDB queue and to be executed once in the queue.\n', - }, - { - name: 'pages_distinct', - type: 'long', - description: 'Approximated count of pages accessed to execute the query.\n', - }, - ], - }, - { - name: 'user', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'host', - type: 'alias', - path: 'source.domain', - migration: true, - }, - { - name: 'ip', - type: 'alias', - path: 'source.ip', - migration: true, - }, - ], - }, - ], - }, - ], - }, - { - key: 'nats', - title: 'nats', - description: 'Module for parsing NATS log files.\n', - release: 'beta', - fields: [ - { - name: 'nats', - type: 'group', - description: 'Fields from NATS logs.\n', - fields: [ - { - name: 'log', - type: 'group', - description: 'Nats log files\n', - release: 'beta', - fields: [ - { - name: 'client', - type: 'group', - description: 'Fields from NATS logs client.\n', - fields: [ - { - name: 'id', - type: 'integer', - description: 'The id of the client\n', - }, - ], - }, - { - name: 'msg', - type: 'group', - description: 'Fields from NATS logs message.\n', - fields: [ - { - name: 'bytes', - type: 'long', - format: 'bytes', - description: 'Size of the payload in bytes\n', - }, - { - name: 'type', - type: 'keyword', - description: 'The protocol message type\n', - }, - { - name: 'subject', - type: 'keyword', - description: 'Subject name this message was received on\n', - }, - { - name: 'sid', - type: 'integer', - description: 'The unique alphanumeric subscription ID of the subject\n', - }, - { - name: 'reply_to', - type: 'keyword', - description: - 'The inbox subject on which the publisher is listening for responses\n', - }, - { - name: 'max_messages', - type: 'integer', - description: - 'An optional number of messages to wait for before automatically unsubscribing\n', - }, - { - name: 'error.message', - type: 'text', - description: 'Details about the error occurred\n', - }, - { - name: 'queue_group', - type: 'text', - description: 'The queue group which subscriber will join\n', - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - key: 'nginx', - title: 'Nginx', - description: 'Module for parsing the Nginx log files.\n', - short_config: true, - fields: [ - { - name: 'nginx', - type: 'group', - description: 'Fields from the Nginx log files.\n', - fields: [ - { - name: 'access', - type: 'group', - description: 'Contains fields for the Nginx access logs.\n', - fields: [ - { - name: 'remote_ip_list', - type: 'array', - description: - 'An array of remote IP addresses. It is a list because it is common to include, besides the client IP address, IP addresses from headers like `X-Forwarded-For`. Real source IP is restored to `source.ip`.\n', - }, - { - name: 'body_sent.bytes', - type: 'alias', - path: 'http.response.body.bytes', - migration: true, - }, - { - name: 'user_name', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'method', - type: 'alias', - path: 'http.request.method', - migration: true, - }, - { - name: 'url', - type: 'alias', - path: 'url.original', - migration: true, - }, - { - name: 'http_version', - type: 'alias', - path: 'http.version', - migration: true, - }, - { - name: 'response_code', - type: 'alias', - path: 'http.response.status_code', - migration: true, - }, - { - name: 'referrer', - type: 'alias', - path: 'http.request.referrer', - migration: true, - }, - { - name: 'agent', - type: 'alias', - path: 'user_agent.original', - migration: true, - }, - { - name: 'user_agent', - type: 'group', - fields: [ - { - name: 'device', - type: 'alias', - path: 'user_agent.device.name', - migration: true, - }, - { - name: 'name', - type: 'alias', - path: 'user_agent.name', - migration: true, - }, - { - name: 'os', - type: 'alias', - path: 'user_agent.os.full_name', - migration: true, - }, - { - name: 'os_name', - type: 'alias', - path: 'user_agent.os.name', - migration: true, - }, - { - name: 'original', - type: 'alias', - path: 'user_agent.original', - migration: true, - }, - ], - }, - { - name: 'geoip', - type: 'group', - fields: [ - { - name: 'continent_name', - type: 'alias', - path: 'source.geo.continent_name', - migration: true, - }, - { - name: 'country_iso_code', - type: 'alias', - path: 'source.geo.country_iso_code', - migration: true, - }, - { - name: 'location', - type: 'alias', - path: 'source.geo.location', - migration: true, - }, - { - name: 'region_name', - type: 'alias', - path: 'source.geo.region_name', - migration: true, - }, - { - name: 'city_name', - type: 'alias', - path: 'source.geo.city_name', - migration: true, - }, - { - name: 'region_iso_code', - type: 'alias', - path: 'source.geo.region_iso_code', - migration: true, - }, - ], - }, - ], - }, - { - name: 'error', - type: 'group', - description: 'Contains fields for the Nginx error logs.\n', - fields: [ - { - name: 'connection_id', - type: 'long', - description: 'Connection identifier.\n', - }, - { - name: 'level', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'pid', - type: 'alias', - path: 'process.pid', - migration: true, - }, - { - name: 'tid', - type: 'alias', - path: 'process.thread.id', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - ], - }, - { - name: 'ingress_controller', - type: 'group', - description: 'Contains fields for the Ingress Nginx controller access logs.\n', - fields: [ - { - name: 'remote_ip_list', - type: 'array', - description: - 'An array of remote IP addresses. It is a list because it is common to include, besides the client IP address, IP addresses from headers like `X-Forwarded-For`. Real source IP is restored to `source.ip`.\n', - }, - { - name: 'http.request.length', - type: 'long', - format: 'bytes', - description: - 'The request length (including request line, header, and request body)\n', - }, - { - name: 'http.request.time', - type: 'double', - format: 'duration', - description: 'Time elapsed since the first bytes were read from the client\n', - }, - { - name: 'upstream.name', - type: 'text', - description: 'The name of the upstream.\n', - }, - { - name: 'upstream.alternative_name', - type: 'text', - description: 'The name of the alternative upstream.\n', - }, - { - name: 'upstream.response.length', - type: 'long', - format: 'bytes', - description: 'The length of the response obtained from the upstream server\n', - }, - { - name: 'upstream.response.time', - type: 'double', - format: 'duration', - description: - 'The time spent on receiving the response from the upstream server as seconds with millisecond resolution\n', - }, - { - name: 'upstream.response.status_code', - type: 'long', - description: 'The status code of the response obtained from the upstream server\n', - }, - { - name: 'http.request.id', - type: 'text', - description: 'The randomly generated ID of the request\n', - }, - { - name: 'upstream.ip', - type: 'ip', - description: - 'The IP address of the upstream server. If several servers were contacted during request processing, their addresses are separated by commas.\n', - }, - { - name: 'upstream.port', - type: 'long', - description: 'The port of the upstream server.\n', - }, - { - name: 'body_sent.bytes', - type: 'alias', - path: 'http.response.body.bytes', - migration: true, - }, - { - name: 'user_name', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'method', - type: 'alias', - path: 'http.request.method', - migration: true, - }, - { - name: 'url', - type: 'alias', - path: 'url.original', - migration: true, - }, - { - name: 'http_version', - type: 'alias', - path: 'http.version', - migration: true, - }, - { - name: 'response_code', - type: 'alias', - path: 'http.response.status_code', - migration: true, - }, - { - name: 'referrer', - type: 'alias', - path: 'http.request.referrer', - migration: true, - }, - { - name: 'agent', - type: 'alias', - path: 'user_agent.original', - migration: true, - }, - { - name: 'user_agent', - type: 'group', - fields: [ - { - name: 'device', - type: 'alias', - path: 'user_agent.device.name', - migration: true, - }, - { - name: 'name', - type: 'alias', - path: 'user_agent.name', - migration: true, - }, - { - name: 'os', - type: 'alias', - path: 'user_agent.os.full_name', - migration: true, - }, - { - name: 'os_name', - type: 'alias', - path: 'user_agent.os.name', - migration: true, - }, - { - name: 'original', - type: 'alias', - path: 'user_agent.original', - migration: true, - }, - ], - }, - { - name: 'geoip', - type: 'group', - fields: [ - { - name: 'continent_name', - type: 'alias', - path: 'source.geo.continent_name', - migration: true, - }, - { - name: 'country_iso_code', - type: 'alias', - path: 'source.geo.country_iso_code', - migration: true, - }, - { - name: 'location', - type: 'alias', - path: 'source.geo.location', - migration: true, - }, - { - name: 'region_name', - type: 'alias', - path: 'source.geo.region_name', - migration: true, - }, - { - name: 'city_name', - type: 'alias', - path: 'source.geo.city_name', - migration: true, - }, - { - name: 'region_iso_code', - type: 'alias', - path: 'source.geo.region_iso_code', - migration: true, - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - key: 'osquery', - title: 'Osquery', - description: 'Fields exported by the `osquery` module\n', - fields: [ - { - name: 'osquery', - type: 'group', - description: '\n', - fields: [ - { - name: 'result', - type: 'group', - description: 'Common fields exported by the result metricset.\n', - fields: [ - { - name: 'name', - type: 'keyword', - description: 'The name of the query that generated this event.\n', - }, - { - name: 'action', - type: 'keyword', - description: - 'For incremental data, marks whether the entry was added or removed. It can be one of "added", "removed", or "snapshot".\n', - }, - { - name: 'host_identifier', - type: 'keyword', - description: - 'The identifier for the host on which the osquery agent is running. Normally the hostname.\n', - }, - { - name: 'unix_time', - type: 'long', - description: - 'Unix timestamp of the event, in seconds since the epoch. Used for computing the `@timestamp` column.\n', - }, - { - name: 'calendar_time', - type: 'keyword', - description: - 'String representation of the collection time, as formatted by osquery.\n', - }, - ], - }, - ], - }, - ], - }, - { - key: 'postgresql', - title: 'PostgreSQL', - description: 'Module for parsing the PostgreSQL log files.\n', - short_config: true, - fields: [ - { - name: 'postgresql', - type: 'group', - description: 'Fields from PostgreSQL logs.\n', - fields: [ - { - name: 'log', - type: 'group', - description: 'Fields from the PostgreSQL log files.\n', - fields: [ - { - name: 'timestamp', - deprecated: '7.3.0', - description: 'The timestamp from the log line.\n', - }, - { - name: 'core_id', - type: 'long', - description: 'Core id\n', - }, - { - name: 'database', - example: 'mydb', - description: 'Name of database\n', - }, - { - name: 'query', - example: 'SELECT * FROM users;', - description: 'Query statement.\n', - }, - { - name: 'query_step', - example: 'parse', - description: - 'Statement step when using extended query protocol (one of statement, parse, bind or execute)\n', - }, - { - name: 'query_name', - example: 'pdo_stmt_00000001', - description: - 'Name given to a query when using extended query protocol. If it is "", or not present, this field is ignored.\n', - }, - { - name: 'error.code', - type: 'long', - description: 'Error code returned by Postgres (if any)', - }, - { - name: 'timezone', - type: 'alias', - path: 'event.timezone', - migration: true, - }, - { - name: 'thread_id', - type: 'alias', - path: 'process.pid', - migration: true, - }, - { - name: 'user', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'level', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - ], - }, - ], - }, - ], - }, - { - key: 'redis', - title: 'Redis', - description: 'Redis Module\n', - fields: [ - { - name: 'redis', - type: 'group', - description: '\n', - fields: [ - { - name: 'log', - type: 'group', - description: 'Redis log files\n', - fields: [ - { - name: 'role', - type: 'keyword', - description: - 'The role of the Redis instance. Can be one of `master`, `slave`, `child` (for RDF/AOF writing child), or `sentinel`.\n', - }, - { - name: 'pid', - type: 'alias', - path: 'process.pid', - migration: true, - }, - { - name: 'level', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - ], - }, - { - name: 'slowlog', - type: 'group', - description: 'Slow logs are retrieved from Redis via a network connection.\n', - fields: [ - { - name: 'cmd', - type: 'keyword', - description: 'The command executed.\n', - }, - { - name: 'duration.us', - type: 'long', - description: 'How long it took to execute the command in microseconds.\n', - }, - { - name: 'id', - type: 'long', - description: 'The ID of the query.\n', - }, - { - name: 'key', - type: 'keyword', - description: 'The key on which the command was executed.\n', - }, - { - name: 'args', - type: 'keyword', - description: 'The arguments with which the command was called.\n', - }, - ], - }, - ], - }, - ], - }, - { - key: 'santa', - title: 'Google Santa', - description: 'Santa Module\n', - fields: [ - { - name: 'santa', - type: 'group', - description: '\n', - fields: [ - { - name: 'action', - type: 'keyword', - example: 'EXEC', - description: 'Action', - }, - { - name: 'decision', - type: 'keyword', - example: 'ALLOW', - description: 'Decision that santad took.', - }, - { - name: 'reason', - type: 'keyword', - example: 'CERT', - description: 'Reason for the decsision.', - }, - { - name: 'mode', - type: 'keyword', - example: 'M', - description: 'Operating mode of Santa.', - }, - { - name: 'disk', - type: 'group', - description: 'Fields for DISKAPPEAR actions.', - fields: [ - { - name: 'volume', - description: 'The volume name.', - }, - { - name: 'bus', - description: 'The disk bus protocol.', - }, - { - name: 'serial', - description: 'The disk serial number.', - }, - { - name: 'bsdname', - example: 'disk1s3', - description: 'The disk BSD name.', - }, - { - name: 'model', - example: 'APPLE SSD SM0512L', - description: 'The disk model.', - }, - { - name: 'fs', - example: 'apfs', - description: 'The disk volume kind (filesystem type).', - }, - { - name: 'mount', - description: 'The disk volume path.', - }, - ], - }, - ], - }, - { - name: 'certificate.common_name', - type: 'keyword', - description: 'Common name from code signing certificate.', - }, - { - name: 'certificate.sha256', - type: 'keyword', - description: 'SHA256 hash of code signing certificate.', - }, - ], - }, - { - key: 'system', - title: 'System', - description: 'Module for parsing system log files.\n', - short_config: true, - fields: [ - { - name: 'system', - type: 'group', - description: 'Fields from the system log files.\n', - fields: [ - { - name: 'auth', - type: 'group', - description: 'Fields from the Linux authorization logs.\n', - fields: [ - { - name: 'timestamp', - type: 'alias', - path: '@timestamp', - migration: true, - }, - { - name: 'hostname', - type: 'alias', - path: 'host.hostname', - migration: true, - }, - { - name: 'program', - type: 'alias', - path: 'process.name', - migration: true, - }, - { - name: 'pid', - type: 'alias', - path: 'process.pid', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - { - name: 'user', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'ssh', - type: 'group', - fields: [ - { - name: 'method', - description: - 'The SSH authentication method. Can be one of "password" or "publickey".\n', - }, - { - name: 'signature', - description: 'The signature of the client public key.\n', - }, - { - name: 'dropped_ip', - type: 'ip', - description: - 'The client IP from SSH connections that are open and immediately dropped.\n', - }, - { - name: 'event', - example: 'Accepted', - description: - 'The SSH event as found in the logs (Accepted, Invalid, Failed, etc.)\n', - }, - { - name: 'ip', - type: 'alias', - path: 'source.ip', - migration: true, - }, - { - name: 'port', - type: 'alias', - path: 'source.port', - migration: true, - }, - { - name: 'geoip', - type: 'group', - fields: [ - { - name: 'continent_name', - type: 'alias', - path: 'source.geo.continent_name', - migration: true, - }, - { - name: 'country_iso_code', - type: 'alias', - path: 'source.geo.country_iso_code', - migration: true, - }, - { - name: 'location', - type: 'alias', - path: 'source.geo.location', - migration: true, - }, - { - name: 'region_name', - type: 'alias', - path: 'source.geo.region_name', - migration: true, - }, - { - name: 'city_name', - type: 'alias', - path: 'source.geo.city_name', - migration: true, - }, - { - name: 'region_iso_code', - type: 'alias', - path: 'source.geo.region_iso_code', - migration: true, - }, - ], - }, - ], - }, - { - name: 'sudo', - type: 'group', - description: 'Fields specific to events created by the `sudo` command.\n', - fields: [ - { - name: 'error', - example: 'user NOT in sudoers', - description: 'The error message in case the sudo command failed.\n', - }, - { - name: 'tty', - description: 'The TTY where the sudo command is executed.\n', - }, - { - name: 'pwd', - description: 'The current directory where the sudo command is executed.\n', - }, - { - name: 'user', - example: 'root', - description: 'The target user to which the sudo command is switching.\n', - }, - { - name: 'command', - description: 'The command executed via sudo.\n', - }, - ], - }, - { - name: 'useradd', - type: 'group', - description: 'Fields specific to events created by the `useradd` command.\n', - fields: [ - { - name: 'home', - description: 'The home folder for the new user.', - }, - { - name: 'shell', - description: 'The default shell for the new user.', - }, - { - name: 'name', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'uid', - type: 'alias', - path: 'user.id', - migration: true, - }, - { - name: 'gid', - type: 'alias', - path: 'group.id', - migration: true, - }, - ], - }, - { - name: 'groupadd', - type: 'group', - description: 'Fields specific to events created by the `groupadd` command.\n', - fields: [ - { - name: 'name', - type: 'alias', - path: 'group.name', - migration: true, - }, - { - name: 'gid', - type: 'alias', - path: 'group.id', - migration: true, - }, - ], - }, - ], - }, - { - name: 'syslog', - type: 'group', - description: 'Contains fields from the syslog system logs.\n', - fields: [ - { - name: 'timestamp', - type: 'alias', - path: '@timestamp', - migration: true, - }, - { - name: 'hostname', - type: 'alias', - path: 'host.hostname', - migration: true, - }, - { - name: 'program', - type: 'alias', - path: 'process.name', - migration: true, - }, - { - name: 'pid', - type: 'alias', - path: 'process.pid', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - ], - }, - ], - }, - ], - }, - { - key: 'traefik', - title: 'Traefik', - description: 'Module for parsing the Traefik log files.\n', - fields: [ - { - name: 'traefik', - type: 'group', - description: 'Fields from the Traefik log files.\n', - fields: [ - { - name: 'access', - type: 'group', - description: 'Contains fields for the Traefik access logs.\n', - fields: [ - { - name: 'user_identifier', - type: 'keyword', - description: 'Is the RFC 1413 identity of the client\n', - }, - { - name: 'request_count', - type: 'long', - description: 'The number of requests\n', - }, - { - name: 'frontend_name', - type: 'keyword', - description: 'The name of the frontend used\n', - }, - { - name: 'backend_url', - type: 'keyword', - description: 'The url of the backend where request is forwarded', - }, - { - name: 'body_sent.bytes', - type: 'alias', - path: 'http.response.body.bytes', - migration: true, - }, - { - name: 'remote_ip', - type: 'alias', - path: 'source.address', - migration: true, - }, - { - name: 'user_name', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'method', - type: 'alias', - path: 'http.request.method', - migration: true, - }, - { - name: 'url', - type: 'alias', - path: 'url.original', - migration: true, - }, - { - name: 'http_version', - type: 'alias', - path: 'http.version', - migration: true, - }, - { - name: 'response_code', - type: 'alias', - path: 'http.response.status_code', - migration: true, - }, - { - name: 'referrer', - type: 'alias', - path: 'http.request.referrer', - migration: true, - }, - { - name: 'agent', - type: 'alias', - path: 'user_agent.original', - migration: true, - }, - { - name: 'user_agent', - type: 'group', - fields: [ - { - name: 'device', - type: 'alias', - path: 'user_agent.device.name', - }, - { - name: 'name', - type: 'alias', - path: 'user_agent.name', - }, - { - name: 'os', - type: 'alias', - path: 'user_agent.os.full_name', - }, - { - name: 'os_name', - type: 'alias', - path: 'user_agent.os.name', - }, - { - name: 'original', - type: 'alias', - path: 'user_agent.original', - }, - ], - }, - { - name: 'geoip', - type: 'group', - fields: [ - { - name: 'continent_name', - type: 'alias', - path: 'source.geo.continent_name', - }, - { - name: 'country_iso_code', - type: 'alias', - path: 'source.geo.country_iso_code', - }, - { - name: 'location', - type: 'alias', - path: 'source.geo.location', - }, - { - name: 'region_name', - type: 'alias', - path: 'source.geo.region_name', - }, - { - name: 'city_name', - type: 'alias', - path: 'source.geo.city_name', - }, - { - name: 'region_iso_code', - type: 'alias', - path: 'source.geo.region_iso_code', - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - key: 'activemq', - title: 'activemq', - release: 'ga', - description: 'Module for parsing ActiveMQ log files.\n', - fields: [ - { - name: 'activemq', - type: 'group', - description: '\n', - fields: [ - { - name: 'caller', - type: 'keyword', - description: 'Name of the caller issuing the logging request (class or resource).\n', - }, - { - name: 'thread', - type: 'keyword', - description: 'Thread that generated the logging event.\n', - }, - { - name: 'user', - type: 'keyword', - description: 'User that generated the logging event.\n', - }, - { - name: 'audit', - type: 'group', - description: 'Fields from ActiveMQ audit logs.\n', - fields: [], - }, - { - name: 'log', - type: 'group', - description: 'Fields from ActiveMQ application logs.\n', - fields: [ - { - name: 'stack_trace', - type: 'keyword', - }, - ], - }, - ], - }, - ], - }, - { - key: 'aws', - title: 'AWS', - release: 'beta', - description: 'Module for handling logs from AWS.\n', - fields: [ - { - name: 'aws', - type: 'group', - description: 'Fields from AWS logs.\n', - fields: [ - { - name: 'cloudtrail', - type: 'group', - release: 'beta', - default_field: false, - description: 'Fields for AWS CloudTrail logs.\n', - fields: [ - { - name: 'event_version', - type: 'keyword', - description: 'The CloudTrail version of the log event format.\n', - }, - { - name: 'user_identity', - type: 'group', - description: - 'The userIdentity element contains details about the type of IAM identity that made the request, and which credentials were used. If temporary credentials were used, the element shows how the credentials were obtained.', - fields: [ - { - name: 'type', - type: 'keyword', - description: 'The type of the identity\n', - }, - { - name: 'arn', - type: 'keyword', - description: - 'The Amazon Resource Name (ARN) of the principal that made the call.', - }, - { - name: 'access_key_id', - type: 'keyword', - description: 'The access key ID that was used to sign the request.', - }, - { - name: 'session_context', - type: 'group', - description: - 'If the request was made with temporary security credentials, an element that provides information about the session that was created for those credentials', - fields: [ - { - name: 'mfa_authenticated', - type: 'keyword', - description: - 'The value is true if the root user or IAM user whose credentials were used for the request also was authenticated with an MFA device; otherwise, false.', - }, - { - name: 'creation_date', - type: 'date', - description: - 'The date and time when the temporary security credentials were issued.', - }, - ], - }, - { - name: 'invoked_by', - type: 'keyword', - description: - 'The name of the AWS service that made the request, such as Amazon EC2 Auto Scaling or AWS Elastic Beanstalk.', - }, - { - name: 'session_issuer', - type: 'group', - description: - 'If the request was made with temporary security credentials, an element that provides information about how the credentials were obtained.', - fields: [ - { - name: 'type', - type: 'keyword', - description: - 'The source of the temporary security credentials, such as Root, IAMUser, or Role.', - }, - { - name: 'principal_id', - type: 'keyword', - description: - 'The internal ID of the entity that was used to get credentials.', - }, - { - name: 'arn', - type: 'keyword', - description: - 'The ARN of the source (account, IAM user, or role) that was used to get temporary security credentials.', - }, - { - name: 'account_id', - type: 'keyword', - description: - 'The account that owns the entity that was used to get credentials.', - }, - ], - }, - ], - }, - { - name: 'error_code', - type: 'keyword', - description: 'The AWS service error if the request returns an error.', - }, - { - name: 'error_message', - type: 'keyword', - description: 'If the request returns an error, the description of the error.', - }, - { - name: 'request_parameters', - type: 'keyword', - description: 'The parameters, if any, that were sent with the request.', - }, - { - name: 'response_elements', - type: 'keyword', - description: - 'The response element for actions that make changes (create, update, or delete actions).', - }, - { - name: 'additional_eventdata', - type: 'keyword', - description: - 'Additional data about the event that was not part of the request or response.', - }, - { - name: 'request_id', - type: 'keyword', - description: - 'The value that identifies the request. The service being called generates this value.', - }, - { - name: 'event_type', - type: 'keyword', - description: 'Identifies the type of event that generated the event record.', - }, - { - name: 'api_version', - type: 'keyword', - description: - 'Identifies the API version associated with the AwsApiCall eventType value.', - }, - { - name: 'management_event', - type: 'keyword', - description: - 'A Boolean value that identifies whether the event is a management event.', - }, - { - name: 'read_only', - type: 'keyword', - description: 'Identifies whether this operation is a read-only operation.', - }, - { - name: 'resources', - type: 'group', - description: 'A list of resources accessed in the event.', - fields: [ - { - name: 'arn', - type: 'keyword', - description: 'Resource ARNs', - }, - { - name: 'account_id', - type: 'keyword', - description: 'Account ID of the resource owner', - }, - { - name: 'type', - type: 'keyword', - description: - 'Resource type identifier in the format: AWS::aws-service-name::data-type-name', - }, - ], - }, - { - name: 'recipient_account_id', - type: 'keyword', - description: 'Represents the account ID that received this event.', - }, - { - name: 'service_event_details', - type: 'keyword', - description: - 'Identifies the service event, including what triggered the event and the result.', - }, - { - name: 'shared_event_id', - type: 'keyword', - description: - 'GUID generated by CloudTrail to uniquely identify CloudTrail events from the same AWS action that is sent to different AWS accounts.', - }, - { - name: 'vpc_endpoint_id', - type: 'keyword', - description: - 'Identifies the VPC endpoint in which requests were made from a VPC to another AWS service, such as Amazon S3.', - }, - { - name: 'console_login', - type: 'group', - description: 'Fields specific to ConsoleLogin events', - fields: [ - { - name: 'additional_eventdata', - type: 'group', - description: 'Additional Event Data for ConsoleLogin events\n', - fields: [ - { - name: 'mobile_version', - type: 'boolean', - description: 'Identifies whether ConsoleLogin was from mobile version', - }, - { - name: 'login_to', - type: 'keyword', - description: 'URL for ConsoleLogin', - }, - { - name: 'mfa_used', - type: 'boolean', - description: - 'Identifies whether multi factor authentication was used during ConsoleLogin', - }, - ], - }, - ], - }, - ], - }, - { - name: 'cloudwatch', - type: 'group', - release: 'beta', - default_field: false, - description: 'Fields for AWS CloudWatch logs.\n', - fields: [], - }, - { - name: 'ec2', - type: 'group', - release: 'beta', - default_field: false, - description: 'Fields for AWS EC2 logs in CloudWatch.\n', - fields: [ - { - name: 'ip_address', - type: 'keyword', - description: 'The internet address of the requester.\n', - }, - ], - }, - { - name: 'elb', - type: 'group', - release: 'ga', - description: 'Fields for AWS ELB logs.\n', - fields: [ - { - name: 'name', - type: 'keyword', - description: 'The name of the load balancer.\n', - }, - { - name: 'type', - type: 'keyword', - description: 'The type of the load balancer for v2 Load Balancers.\n', - }, - { - name: 'target_group.arn', - type: 'keyword', - description: 'The ARN of the target group handling the request.\n', - }, - { - name: 'listener', - type: 'keyword', - description: 'The ELB listener that received the connection.\n', - }, - { - name: 'protocol', - type: 'keyword', - description: 'The protocol of the load balancer (http or tcp).\n', - }, - { - name: 'request_processing_time.sec', - type: 'float', - description: - 'The total time in seconds since the connection or request is received until it is sent to a registered backend.\n', - }, - { - name: 'backend_processing_time.sec', - type: 'float', - description: - 'The total time in seconds since the connection is sent to the backend till the backend starts responding.\n', - }, - { - name: 'response_processing_time.sec', - type: 'float', - description: - 'The total time in seconds since the response is received from the backend till it is sent to the client.\n', - }, - { - name: 'connection_time.ms', - type: 'long', - description: - 'The total time of the connection in milliseconds, since it is opened till it is closed.\n', - }, - { - name: 'tls_handshake_time.ms', - type: 'long', - description: - 'The total time for the TLS handshake to complete in milliseconds once the connection has been established.\n', - }, - { - name: 'backend.ip', - type: 'keyword', - description: 'The IP address of the backend processing this connection.\n', - }, - { - name: 'backend.port', - type: 'keyword', - description: 'The port in the backend processing this connection.\n', - }, - { - name: 'backend.http.response.status_code', - type: 'keyword', - description: - 'The status code from the backend (status code sent to the client from ELB is stored in `http.response.status_code`\n', - }, - { - name: 'ssl_cipher', - type: 'keyword', - description: 'The SSL cipher used in TLS/SSL connections.\n', - }, - { - name: 'ssl_protocol', - type: 'keyword', - description: 'The SSL protocol used in TLS/SSL connections.\n', - }, - { - name: 'chosen_cert.arn', - type: 'keyword', - description: - 'The ARN of the chosen certificate presented to the client in TLS/SSL connections.\n', - }, - { - name: 'chosen_cert.serial', - type: 'keyword', - description: - 'The serial number of the chosen certificate presented to the client in TLS/SSL connections.\n', - }, - { - name: 'incoming_tls_alert', - type: 'keyword', - description: - 'The integer value of TLS alerts received by the load balancer from the client, if present.\n', - }, - { - name: 'tls_named_group', - type: 'keyword', - description: 'The TLS named group.\n', - }, - { - name: 'trace_id', - type: 'keyword', - description: 'The contents of the `X-Amzn-Trace-Id` header.\n', - }, - { - name: 'matched_rule_priority', - type: 'keyword', - description: - 'The priority value of the rule that matched the request, if a rule matched.\n', - }, - { - name: 'action_executed', - type: 'keyword', - description: - 'The action executed when processing the request (forward, fixed-response, authenticate...). It can contain several values.\n', - }, - { - name: 'redirect_url', - type: 'keyword', - description: 'The URL used if a redirection action was executed.\n', - }, - { - name: 'error.reason', - type: 'keyword', - description: 'The error reason if the executed action failed.', - }, - ], - }, - { - name: 's3access', - type: 'group', - release: 'ga', - description: 'Fields for AWS S3 server access logs.\n', - fields: [ - { - name: 'bucket_owner', - type: 'keyword', - description: 'The canonical user ID of the owner of the source bucket.\n', - }, - { - name: 'bucket', - type: 'keyword', - description: 'The name of the bucket that the request was processed against.\n', - }, - { - name: 'remote_ip', - type: 'ip', - description: 'The apparent internet address of the requester.\n', - }, - { - name: 'requester', - type: 'keyword', - description: - 'The canonical user ID of the requester, or a - for unauthenticated requests.\n', - }, - { - name: 'request_id', - type: 'keyword', - description: 'A string generated by Amazon S3 to uniquely identify each request.\n', - }, - { - name: 'operation', - type: 'keyword', - description: - 'The operation listed here is declared as SOAP.operation, REST.HTTP_method.resource_type, WEBSITE.HTTP_method.resource_type, or BATCH.DELETE.OBJECT.\n', - }, - { - name: 'key', - type: 'keyword', - description: - 'The "key" part of the request, URL encoded, or "-" if the operation does not take a key parameter.\n', - }, - { - name: 'request_uri', - type: 'keyword', - description: 'The Request-URI part of the HTTP request message.\n', - }, - { - name: 'http_status', - type: 'long', - description: 'The numeric HTTP status code of the response.\n', - }, - { - name: 'error_code', - type: 'keyword', - description: 'The Amazon S3 Error Code, or "-" if no error occurred.\n', - }, - { - name: 'bytes_sent', - type: 'long', - description: - 'The number of response bytes sent, excluding HTTP protocol overhead, or "-" if zero.\n', - }, - { - name: 'object_size', - type: 'long', - description: 'The total size of the object in question.\n', - }, - { - name: 'total_time', - type: 'long', - description: - "The number of milliseconds the request was in flight from the server's perspective.\n", - }, - { - name: 'turn_around_time', - type: 'long', - description: - 'The number of milliseconds that Amazon S3 spent processing your request.\n', - }, - { - name: 'referrer', - type: 'keyword', - description: 'The value of the HTTP Referrer header, if present.\n', - }, - { - name: 'user_agent', - type: 'keyword', - description: 'The value of the HTTP User-Agent header.\n', - }, - { - name: 'version_id', - type: 'keyword', - description: - 'The version ID in the request, or "-" if the operation does not take a versionId parameter.\n', - }, - { - name: 'host_id', - type: 'keyword', - description: 'The x-amz-id-2 or Amazon S3 extended request ID.\n', - }, - { - name: 'signature_version', - type: 'keyword', - description: - 'The signature version, SigV2 or SigV4, that was used to authenticate the request or a - for unauthenticated requests.\n', - }, - { - name: 'cipher_suite', - type: 'keyword', - description: - 'The Secure Sockets Layer (SSL) cipher that was negotiated for HTTPS request or a - for HTTP.\n', - }, - { - name: 'authentication_type', - type: 'keyword', - description: - 'The type of request authentication used, AuthHeader for authentication headers, QueryString for query string (pre-signed URL) or a - for unauthenticated requests.\n', - }, - { - name: 'host_header', - type: 'keyword', - description: 'The endpoint used to connect to Amazon S3.\n', - }, - { - name: 'tls_version', - type: 'keyword', - description: - 'The Transport Layer Security (TLS) version negotiated by the client.\n', - }, - ], - }, - { - name: 'vpcflow', - type: 'group', - release: 'beta', - description: 'Fields for AWS VPC flow logs.\n', - fields: [ - { - name: 'version', - type: 'keyword', - description: - 'The VPC Flow Logs version. If you use the default format, the version is 2. If you specify a custom format, the version is 3.\n', - }, - { - name: 'account_id', - type: 'keyword', - description: 'The AWS account ID for the flow log.\n', - }, - { - name: 'interface_id', - type: 'keyword', - description: 'The ID of the network interface for which the traffic is recorded.\n', - }, - { - name: 'action', - type: 'keyword', - description: 'The action that is associated with the traffic, ACCEPT or REJECT.\n', - }, - { - name: 'log_status', - type: 'keyword', - description: 'The logging status of the flow log, OK, NODATA or SKIPDATA.\n', - }, - { - name: 'instance_id', - type: 'keyword', - description: - "The ID of the instance that's associated with network interface for which the traffic is recorded, if the instance is owned by you.\n", - }, - { - name: 'pkt_srcaddr', - type: 'ip', - description: 'The packet-level (original) source IP address of the traffic.\n', - }, - { - name: 'pkt_dstaddr', - type: 'ip', - description: - 'The packet-level (original) destination IP address for the traffic.\n', - }, - { - name: 'vpc_id', - type: 'keyword', - description: - 'The ID of the VPC that contains the network interface for which the traffic is recorded.\n', - }, - { - name: 'subnet_id', - type: 'keyword', - description: - 'The ID of the subnet that contains the network interface for which the traffic is recorded.\n', - }, - { - name: 'tcp_flags', - type: 'keyword', - description: - 'The bitmask value for the following TCP flags: 2=SYN,18=SYN-ACK,1=FIN,4=RST\n', - }, - { - name: 'type', - type: 'keyword', - description: 'The type of traffic: IPv4, IPv6, or EFA.\n', - }, - ], - }, - ], - }, - ], - }, - { - key: 'azure', - title: 'Azure', - release: 'beta', - description: 'Azure Module\n', - fields: [ - { - name: 'azure', - type: 'group', - description: '\n', - fields: [ - { - name: 'subscription_id', - type: 'keyword', - description: 'Azure subscription ID\n', - }, - { - name: 'correlation_id', - type: 'keyword', - description: 'Correlation ID\n', - }, - { - name: 'tenant_id', - type: 'keyword', - description: 'tenant ID\n', - }, - { - name: 'resource', - type: 'group', - description: 'Resource\n', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'Resource ID\n', - }, - { - name: 'group', - type: 'keyword', - description: 'Resource group\n', - }, - { - name: 'provider', - type: 'keyword', - description: 'Resource type/namespace\n', - }, - { - name: 'namespace', - type: 'keyword', - description: 'Resource type/namespace\n', - }, - { - name: 'name', - type: 'keyword', - description: 'Name\n', - }, - { - name: 'authorization_rule', - type: 'keyword', - description: 'Authorization rule\n', - }, - ], - }, - { - name: 'activitylogs', - type: 'group', - release: 'beta', - description: 'Fields for Azure activity logs.\n', - fields: [ - { - name: 'identity', - type: 'group', - description: 'Identity\n', - fields: [ - { - name: 'claims_initiated_by_user', - type: 'group', - description: 'Claims initiated by user\n', - fields: [ - { - name: 'name', - type: 'keyword', - description: 'Name\n', - }, - { - name: 'givenname', - type: 'keyword', - description: 'Givenname\n', - }, - { - name: 'surname', - type: 'keyword', - description: 'Surname\n', - }, - { - name: 'fullname', - type: 'keyword', - description: 'Fullname\n', - }, - { - name: 'schema', - type: 'keyword', - description: 'Schema\n', - }, - ], - }, - { - name: 'claims.*', - type: 'object', - object_type: 'keyword', - object_type_mapping_type: '*', - description: 'Claims\n', - }, - { - name: 'authorization', - type: 'group', - description: 'Authorization\n', - fields: [ - { - name: 'scope', - type: 'keyword', - description: 'Scope\n', - }, - { - name: 'action', - type: 'keyword', - description: 'Action\n', - }, - { - name: 'evidence', - type: 'group', - description: 'Evidence\n', - fields: [ - { - name: 'role_assignment_scope', - type: 'keyword', - description: 'Role assignment scope\n', - }, - { - name: 'role_definition_id', - type: 'keyword', - description: 'Role definition ID\n', - }, - { - name: 'role', - type: 'keyword', - description: 'Role\n', - }, - { - name: 'role_assignment_id', - type: 'keyword', - description: 'Role assignment ID\n', - }, - { - name: 'principal_id', - type: 'keyword', - description: 'Principal ID\n', - }, - { - name: 'principal_type', - type: 'keyword', - description: 'Principal type\n', - }, - ], - }, - ], - }, - ], - }, - { - name: 'operation_name', - type: 'keyword', - description: 'Operation name\n', - }, - { - name: 'result_signature', - type: 'keyword', - description: 'Result signature\n', - }, - { - name: 'category', - type: 'keyword', - description: 'Category\n', - }, - { - name: 'properties', - type: 'group', - description: 'Properties\n', - fields: [ - { - name: 'service_request_id', - type: 'keyword', - description: 'Service Request Id\n', - }, - { - name: 'status_code', - type: 'keyword', - description: 'Status code\n', - }, - ], - }, - ], - }, - { - name: 'auditlogs', - type: 'group', - description: 'Fields for Azure audit logs.\n', - fields: [ - { - name: 'operation_name', - type: 'keyword', - description: 'The operation name\n', - }, - { - name: 'operation_version', - type: 'keyword', - description: 'The operation version\n', - }, - { - name: 'identity', - type: 'keyword', - description: 'Identity\n', - }, - { - name: 'tenant_id', - type: 'keyword', - description: 'Tenant ID\n', - }, - { - name: 'result_signature', - type: 'keyword', - description: 'Result signature\n', - }, - { - name: 'properties', - type: 'group', - description: 'The audit log properties\n', - fields: [ - { - name: 'result', - type: 'keyword', - description: 'Log result\n', - }, - { - name: 'activity_display_name', - type: 'keyword', - description: 'Activity display name\n', - }, - { - name: 'result_reason', - type: 'keyword', - description: 'Reason for the log result\n', - }, - { - name: 'correlation_id', - type: 'keyword', - description: 'Correlation ID\n', - }, - { - name: 'logged_by_service', - type: 'keyword', - description: 'Logged by service\n', - }, - { - name: 'operation_type', - type: 'keyword', - description: 'Operation type\n', - }, - { - name: 'id', - type: 'keyword', - description: 'ID\n', - }, - { - name: 'activity_datetime', - type: 'date', - description: 'Activity timestamp\n', - }, - { - name: 'category', - type: 'keyword', - description: 'category\n', - }, - { - name: 'target_resources.*', - type: 'group', - object_type_mapping_type: '*', - description: 'Target resources\n', - fields: [ - { - name: 'display_name', - type: 'keyword', - description: 'Display name\n', - }, - { - name: 'id', - type: 'keyword', - description: 'ID\n', - }, - { - name: 'type', - type: 'keyword', - description: 'Type\n', - }, - { - name: 'ip_address', - type: 'keyword', - description: 'ip Address\n', - }, - { - name: 'user_principal_name', - type: 'keyword', - description: 'User principal name\n', - }, - { - name: 'modified_properties.*', - type: 'group', - object_type: 'keyword', - object_type_mapping_type: '*', - description: 'Modified properties\n', - fields: [ - { - name: 'new_value', - type: 'keyword', - description: 'New value\n', - }, - { - name: 'display_name', - type: 'keyword', - description: 'Display value\n', - }, - { - name: 'old_value', - type: 'keyword', - description: 'Old value\n', - }, - ], - }, - ], - }, - { - name: 'initiated_by', - type: 'group', - description: 'Information regarding the initiator\n', - fields: [ - { - name: 'app', - type: 'group', - description: 'App\n', - fields: [ - { - name: 'servicePrincipalName', - type: 'keyword', - description: 'Service principal name\n', - }, - { - name: 'displayName', - type: 'keyword', - description: 'Display name\n', - }, - { - name: 'appId', - type: 'keyword', - description: 'App ID\n', - }, - { - name: 'servicePrincipalId', - type: 'keyword', - description: 'Service principal ID\n', - }, - ], - }, - { - name: 'user', - type: 'group', - description: 'User\n', - fields: [ - { - name: 'userPrincipalName', - type: 'keyword', - description: 'User principal name\n', - }, - { - name: 'displayName', - type: 'keyword', - description: 'Display name\n', - }, - { - name: 'id', - type: 'keyword', - description: 'ID\n', - }, - { - name: 'ipAddress', - type: 'keyword', - description: 'ip Address\n', - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - name: 'signinlogs', - type: 'group', - description: 'Fields for Azure sign-in logs.\n', - fields: [ - { - name: 'operation_name', - type: 'keyword', - description: 'The operation name\n', - }, - { - name: 'operation_version', - type: 'keyword', - description: 'The operation version\n', - }, - { - name: 'tenant_id', - type: 'keyword', - description: 'Tenant ID\n', - }, - { - name: 'result_signature', - type: 'keyword', - description: 'Result signature\n', - }, - { - name: 'result_description', - type: 'keyword', - description: 'Result description\n', - }, - { - name: 'identity', - type: 'keyword', - description: 'Identity\n', - }, - { - name: 'properties', - type: 'group', - description: 'The signin log properties\n', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'ID\n', - }, - { - name: 'created_at', - type: 'date', - description: 'Created date time\n', - }, - { - name: 'user_display_name', - type: 'keyword', - description: 'User display name\n', - }, - { - name: 'correlation_id', - type: 'keyword', - description: 'Correlation ID\n', - }, - { - name: 'user_principal_name', - type: 'keyword', - description: 'User principal name\n', - }, - { - name: 'user_id', - type: 'keyword', - description: 'User ID\n', - }, - { - name: 'app_id', - type: 'keyword', - description: 'App ID\n', - }, - { - name: 'app_display_name', - type: 'keyword', - description: 'App display name\n', - }, - { - name: 'ip_address', - type: 'keyword', - description: 'Ip address\n', - }, - { - name: 'client_app_used', - type: 'keyword', - description: 'Client app used\n', - }, - { - name: 'conditional_access_status', - type: 'keyword', - description: 'Conditional access status\n', - }, - { - name: 'original_request_id', - type: 'keyword', - description: 'Original request ID\n', - }, - { - name: 'is_interactive', - type: 'keyword', - description: 'Is interactive\n', - }, - { - name: 'token_issuer_name', - type: 'keyword', - description: 'Token issuer name\n', - }, - { - name: 'token_issuer_type', - type: 'keyword', - description: 'Token issuer type\n', - }, - { - name: 'processing_time_ms', - type: 'float', - description: 'Processing time in milliseconds\n', - }, - { - name: 'risk_detail', - type: 'keyword', - description: 'Risk detail\n', - }, - { - name: 'risk_level_aggregated', - type: 'keyword', - description: 'Risk level aggregated\n', - }, - { - name: 'risk_level_during_signin', - type: 'keyword', - description: 'Risk level during signIn\n', - }, - { - name: 'risk_state', - type: 'keyword', - description: 'Risk state\n', - }, - { - name: 'resource_display_name', - type: 'keyword', - description: 'Resource display name\n', - }, - { - name: 'status', - type: 'group', - description: 'Status\n', - fields: [ - { - name: 'error_code', - type: 'keyword', - description: 'Error code\n', - }, - ], - }, - { - name: 'device_detail', - type: 'group', - description: 'Status\n', - fields: [ - { - name: 'device_id', - type: 'keyword', - description: 'Device ID\n', - }, - { - name: 'operating_system', - type: 'keyword', - description: 'Operating system\n', - }, - { - name: 'browser', - type: 'keyword', - description: 'Browser\n', - }, - { - name: 'display_name', - type: 'keyword', - description: 'Display name\n', - }, - { - name: 'trust_type', - type: 'keyword', - description: 'Trust type\n', - }, - ], - }, - { - name: 'service_principal_id', - type: 'keyword', - description: 'Status\n', - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - key: 'cef-module', - title: 'CEF', - description: - 'Module for receiving CEF logs over Syslog. The module adds vendor specific fields in addition to the fields the decode_cef processor provides.\n', - fields: [ - { - name: 'forcepoint', - type: 'group', - default_field: false, - description: 'Fields for Forcepoint Custom String mappings\n', - fields: [ - { - name: 'virus_id', - type: 'keyword', - description: 'Virus ID\n', - }, - ], - }, - { - name: 'checkpoint', - type: 'group', - default_field: false, - description: 'Fields for Check Point custom string mappings.\n', - fields: [ - { - name: 'app_risk', - type: 'keyword', - description: 'Application risk.', - }, - { - name: 'app_severity', - type: 'keyword', - description: 'Application threat severity.', - }, - { - name: 'app_sig_id', - type: 'keyword', - description: 'The signature ID which the application was detected by.', - }, - { - name: 'auth_method', - type: 'keyword', - description: 'Password authentication protocol used.', - }, - { - name: 'category', - type: 'keyword', - description: 'Category.', - }, - { - name: 'confidence_level', - type: 'keyword', - description: 'Confidence level determined.', - }, - { - name: 'connectivity_state', - type: 'keyword', - description: 'Connectivity state.', - }, - { - name: 'cookie', - type: 'keyword', - description: 'IKE cookie.', - }, - { - name: 'dst_phone_number', - type: 'keyword', - description: 'Destination IP-Phone.', - }, - { - name: 'email_control', - type: 'keyword', - description: 'Engine name.', - }, - { - name: 'email_id', - type: 'keyword', - description: 'Internal email ID.', - }, - { - name: 'email_recipients_num', - type: 'long', - description: 'Number of recipients.', - }, - { - name: 'email_session_id', - type: 'keyword', - description: 'Internal email session ID.', - }, - { - name: 'email_spool_id', - type: 'keyword', - description: 'Internal email spool ID.', - }, - { - name: 'email_subject', - type: 'keyword', - description: 'Email subject.', - }, - { - name: 'event_count', - type: 'long', - description: 'Number of events associated with the log.', - }, - { - name: 'file_hash', - type: 'keyword', - description: 'File hash (SHA1 or MD5).', - }, - { - name: 'frequency', - type: 'keyword', - description: 'Scan frequency.', - }, - { - name: 'icmp_type', - type: 'long', - description: 'ICMP type.', - }, - { - name: 'icmp_code', - type: 'long', - description: 'ICMP code.', - }, - { - name: 'identity_type', - type: 'keyword', - description: 'Identity type.', - }, - { - name: 'incident_extension', - type: 'keyword', - description: 'Format of original data.', - }, - { - name: 'integrity_av_invoke_type', - type: 'keyword', - description: 'Scan invoke type.', - }, - { - name: 'peer_gateway', - type: 'ip', - description: 'Main IP of the peer Security Gateway.', - }, - { - name: 'performance_impact', - type: 'keyword', - description: 'Protection performance impact.', - }, - { - name: 'protection_id', - type: 'keyword', - description: 'Protection malware ID.', - }, - { - name: 'protection_name', - type: 'keyword', - description: 'Specific signature name of the attack.', - }, - { - name: 'protection_type', - type: 'keyword', - description: 'Type of protection used to detect the attack.', - }, - { - name: 'scan_result', - type: 'keyword', - description: 'Scan result.', - }, - { - name: 'sensor_mode', - type: 'keyword', - description: 'Sensor mode.', - }, - { - name: 'severity', - type: 'keyword', - description: 'Threat severity.', - }, - { - name: 'malware_status', - type: 'keyword', - description: 'Malware status.', - }, - { - name: 'subscription_expiration', - type: 'date', - description: 'The expiration date of the subscription.', - }, - { - name: 'tcp_flags', - type: 'keyword', - description: 'TCP packet flags.', - }, - { - name: 'termination_reason', - type: 'keyword', - description: 'Termination reason.', - }, - { - name: 'update_status', - type: 'keyword', - description: 'Update status.', - }, - { - name: 'user_status', - type: 'keyword', - description: 'User response.', - }, - { - name: 'uuid', - type: 'keyword', - description: 'External ID.', - }, - { - name: 'virus_name', - type: 'keyword', - description: 'Virus name.', - }, - { - name: 'malware_name', - type: 'keyword', - description: 'Malware name.', - }, - { - name: 'malware_family', - type: 'keyword', - description: 'Malware family.', - }, - { - name: 'voip_log_type', - type: 'keyword', - description: 'VoIP log types.', - }, - ], - }, - { - name: 'cef.extensions', - type: 'group', - default_field: false, - description: 'Extra vendor-specific extensions.\n', - fields: [ - { - name: 'cp_app_risk', - type: 'keyword', - }, - { - name: 'cp_severity', - type: 'keyword', - }, - { - name: 'ifname', - type: 'keyword', - }, - { - name: 'inzone', - type: 'keyword', - }, - { - name: 'layer_uuid', - type: 'keyword', - }, - { - name: 'layer_name', - type: 'keyword', - }, - { - name: 'logid', - type: 'keyword', - }, - { - name: 'loguid', - type: 'keyword', - }, - { - name: 'match_id', - type: 'keyword', - }, - { - name: 'nat_addtnl_rulenum', - type: 'keyword', - }, - { - name: 'nat_rulenum', - type: 'keyword', - }, - { - name: 'origin', - type: 'keyword', - }, - { - name: 'originsicname', - type: 'keyword', - }, - { - name: 'outzone', - type: 'keyword', - }, - { - name: 'parent_rule', - type: 'keyword', - }, - { - name: 'product', - type: 'keyword', - }, - { - name: 'rule_action', - type: 'keyword', - }, - { - name: 'rule_uid', - type: 'keyword', - }, - { - name: 'sequencenum', - type: 'keyword', - }, - { - name: 'service_id', - type: 'keyword', - }, - { - name: 'version', - type: 'keyword', - }, - ], - }, - ], - }, - { - key: 'cisco', - title: 'Cisco', - description: 'Module for handling Cisco network device logs.\n', - fields: [ - { - name: 'cisco', - type: 'group', - description: 'Fields from Cisco logs.\n', - fields: [ - { - name: 'asa', - type: 'group', - description: 'Fields for Cisco ASA Firewall.\n', - fields: [ - { - name: 'message_id', - type: 'keyword', - description: 'The Cisco ASA message identifier.\n', - }, - { - name: 'suffix', - type: 'keyword', - example: 'session', - description: 'Optional suffix after %ASA identifier.\n', - }, - { - name: 'source_interface', - type: 'keyword', - description: 'Source interface for the flow or event.\n', - }, - { - name: 'destination_interface', - type: 'keyword', - description: 'Destination interface for the flow or event.\n', - }, - { - name: 'rule_name', - type: 'keyword', - description: 'Name of the Access Control List rule that matched this event.\n', - }, - { - name: 'source_username', - type: 'keyword', - description: 'Name of the user that is the source for this event.\n', - }, - { - name: 'destination_username', - type: 'keyword', - description: 'Name of the user that is the destination for this event.\n', - }, - { - name: 'mapped_source_ip', - type: 'ip', - description: 'The translated source IP address.\n', - }, - { - name: 'mapped_source_port', - type: 'long', - description: 'The translated source port.\n', - }, - { - name: 'mapped_destination_ip', - type: 'ip', - description: 'The translated destination IP address.\n', - }, - { - name: 'mapped_destination_port', - type: 'long', - description: 'The translated destination port.\n', - }, - { - name: 'threat_level', - type: 'keyword', - description: - 'Threat level for malware / botnet traffic. One of very-low, low, moderate, high or very-high.\n', - }, - { - name: 'threat_category', - type: 'keyword', - description: - 'Category for the malware / botnet traffic. For example: virus, botnet, trojan, etc.\n', - }, - { - name: 'connection_id', - type: 'keyword', - description: 'Unique identifier for a flow.\n', - }, - { - name: 'icmp_type', - type: 'short', - description: 'ICMP type.\n', - }, - { - name: 'icmp_code', - type: 'short', - description: 'ICMP code.\n', - }, - { - name: 'connection_type', - type: 'keyword', - default_field: false, - description: 'The VPN connection type\n', - }, - { - name: 'dap_records', - default_field: false, - type: 'keyword', - description: 'The assigned DAP records\n', - }, - ], - }, - { - name: 'ftd', - type: 'group', - description: 'Fields for Cisco Firepower Threat Defense Firewall.\n', - fields: [ - { - name: 'message_id', - type: 'keyword', - description: 'The Cisco FTD message identifier.\n', - }, - { - name: 'suffix', - type: 'keyword', - example: 'session', - description: 'Optional suffix after %FTD identifier.\n', - }, - { - name: 'source_interface', - type: 'keyword', - description: 'Source interface for the flow or event.\n', - }, - { - name: 'destination_interface', - type: 'keyword', - description: 'Destination interface for the flow or event.\n', - }, - { - name: 'rule_name', - type: 'keyword', - description: 'Name of the Access Control List rule that matched this event.\n', - }, - { - name: 'source_username', - type: 'keyword', - description: 'Name of the user that is the source for this event.\n', - }, - { - name: 'destination_username', - type: 'keyword', - description: 'Name of the user that is the destination for this event.\n', - }, - { - name: 'mapped_source_ip', - type: 'ip', - description: 'The translated source IP address. Use ECS source.nat.ip.\n', - }, - { - name: 'mapped_source_port', - type: 'long', - description: 'The translated source port. Use ECS source.nat.port.\n', - }, - { - name: 'mapped_destination_ip', - type: 'ip', - description: 'The translated destination IP address. Use ECS destination.nat.ip.\n', - }, - { - name: 'mapped_destination_port', - type: 'long', - description: 'The translated destination port. Use ECS destination.nat.port.\n', - }, - { - name: 'threat_level', - type: 'keyword', - description: - 'Threat level for malware / botnet traffic. One of very-low, low, moderate, high or very-high.\n', - }, - { - name: 'threat_category', - type: 'keyword', - description: - 'Category for the malware / botnet traffic. For example: virus, botnet, trojan, etc.\n', - }, - { - name: 'connection_id', - type: 'keyword', - description: 'Unique identifier for a flow.\n', - }, - { - name: 'icmp_type', - type: 'short', - description: 'ICMP type.\n', - }, - { - name: 'icmp_code', - type: 'short', - description: 'ICMP code.\n', - }, - { - name: 'security', - type: 'object', - description: 'Raw fields for Security Events.', - }, - { - name: 'connection_type', - type: 'keyword', - default_field: false, - description: 'The VPN connection type\n', - }, - { - name: 'dap_records', - type: 'keyword', - default_field: false, - description: 'The assigned DAP records\n', - }, - ], - }, - { - name: 'ios', - type: 'group', - description: 'Fields for Cisco IOS logs.\n', - fields: [ - { - name: 'access_list', - type: 'keyword', - description: 'Name of the IP access list.\n', - }, - { - name: 'facility', - type: 'keyword', - example: 'SEC', - description: - 'The facility to which the message refers (for example, SNMP, SYS, and so forth). A facility can be a hardware device, a protocol, or a module of the system software. It denotes the source or the cause of the system message.\n', - }, - ], - }, - ], - }, - ], - }, - { - key: 'coredns', - title: 'Coredns', - description: 'Module for handling logs produced by coredns\n', - fields: [ - { - name: 'coredns', - type: 'group', - description: 'coredns fields after normalization\n', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'id of the DNS transaction\n', - }, - { - name: 'query.size', - type: 'integer', - format: 'bytes', - description: 'size of the DNS query\n', - }, - { - name: 'query.class', - type: 'keyword', - description: 'DNS query class\n', - }, - { - name: 'query.name', - type: 'keyword', - description: 'DNS query name\n', - }, - { - name: 'query.type', - type: 'keyword', - description: 'DNS query type\n', - }, - { - name: 'response.code', - type: 'keyword', - description: 'DNS response code\n', - }, - { - name: 'response.flags', - type: 'keyword', - description: 'DNS response flags\n', - }, - { - name: 'response.size', - type: 'integer', - format: 'bytes', - description: 'size of the DNS response\n', - }, - { - name: 'dnssec_ok', - type: 'boolean', - description: 'dnssec flag\n', - }, - ], - }, - ], - }, - { - key: 'envoyproxy', - title: 'Envoyproxy', - description: 'Module for handling logs produced by envoy\n', - fields: [ - { - name: 'envoyproxy', - type: 'group', - description: 'Fields from envoy proxy logs after normalization\n', - fields: [ - { - name: 'log_type', - type: 'keyword', - description: 'Envoy log type, normally ACCESS\n', - }, - { - name: 'response_flags', - type: 'keyword', - description: 'Response flags\n', - }, - { - name: 'upstream_service_time', - type: 'long', - format: 'duration', - input_format: 'nanoseconds', - description: 'Upstream service time in nanoseconds\n', - }, - { - name: 'request_id', - type: 'keyword', - description: 'ID of the request\n', - }, - { - name: 'authority', - type: 'keyword', - description: 'Envoy proxy authority field\n', - }, - { - name: 'proxy_type', - type: 'keyword', - description: 'Envoy proxy type, tcp or http\n', - }, - ], - }, - ], - }, - { - key: 'googlecloud', - title: 'Google Cloud', - description: 'Module for handling logs from Google Cloud.\n', - fields: [ - { - name: 'googlecloud', - type: 'group', - description: 'Fields from Google Cloud logs.\n', - fields: [ - { - name: 'destination.instance', - type: 'group', - description: - 'If the destination of the connection was a VM located on the same VPC, this field is populated with VM instance details. In a Shared VPC configuration, project_id corresponds to the project that owns the instance, usually the service project.\n', - fields: [ - { - name: 'project_id', - type: 'keyword', - description: 'ID of the project containing the VM.\n', - }, - { - name: 'region', - type: 'keyword', - description: 'Region of the VM.\n', - }, - { - name: 'zone', - type: 'keyword', - description: 'Zone of the VM.\n', - }, - ], - }, - { - name: 'destination.vpc', - type: 'group', - description: - 'If the destination of the connection was a VM located on the same VPC, this field is populated with VPC network details. In a Shared VPC configuration, project_id corresponds to that of the host project.\n', - fields: [ - { - name: 'project_id', - type: 'keyword', - description: 'ID of the project containing the VM.\n', - }, - { - name: 'vpc_name', - type: 'keyword', - description: 'VPC on which the VM is operating.\n', - }, - { - name: 'subnetwork_name', - type: 'keyword', - description: 'Subnetwork on which the VM is operating.\n', - }, - ], - }, - { - name: 'source.instance', - type: 'group', - description: - 'If the source of the connection was a VM located on the same VPC, this field is populated with VM instance details. In a Shared VPC configuration, project_id corresponds to the project that owns the instance, usually the service project.\n', - fields: [ - { - name: 'project_id', - type: 'keyword', - description: 'ID of the project containing the VM.\n', - }, - { - name: 'region', - type: 'keyword', - description: 'Region of the VM.\n', - }, - { - name: 'zone', - type: 'keyword', - description: 'Zone of the VM.\n', - }, - ], - }, - { - name: 'source.vpc', - type: 'group', - description: - 'If the source of the connection was a VM located on the same VPC, this field is populated with VPC network details. In a Shared VPC configuration, project_id corresponds to that of the host project.\n', - fields: [ - { - name: 'project_id', - type: 'keyword', - description: 'ID of the project containing the VM.\n', - }, - { - name: 'vpc_name', - type: 'keyword', - description: 'VPC on which the VM is operating.\n', - }, - { - name: 'subnetwork_name', - type: 'keyword', - description: 'Subnetwork on which the VM is operating.\n', - }, - ], - }, - { - name: 'audit', - type: 'group', - description: 'Fields for Google Cloud audit logs.\n', - fields: [ - { - name: 'type', - type: 'keyword', - description: 'Type property.\n', - }, - { - name: 'authentication_info', - type: 'group', - description: 'Authentication information.\n', - fields: [ - { - name: 'principal_email', - type: 'keyword', - description: - 'The email address of the authenticated user making the request.\n', - }, - { - name: 'authority_selector', - type: 'keyword', - description: - 'The authority selector specified by the requestor, if any. It is not guaranteed that the principal was allowed to use this authority.\n', - }, - ], - }, - { - name: 'authorization_info', - type: 'array', - description: 'Authorization information for the operation.\n', - fields: [ - { - name: 'permission', - type: 'keyword', - description: 'The required IAM permission.\n', - }, - { - name: 'granted', - type: 'boolean', - description: - 'Whether or not authorization for resource and permission was granted.\n', - }, - { - name: 'resource_attributes', - type: 'group', - description: 'The attributes of the resource.\n', - fields: [ - { - name: 'service', - type: 'keyword', - description: 'The name of the service.\n', - }, - { - name: 'name', - type: 'keyword', - description: 'The name of the resource.\n', - }, - { - name: 'type', - type: 'keyword', - description: 'The type of the resource.\n', - }, - ], - }, - ], - }, - { - name: 'method_name', - type: 'keyword', - description: - "The name of the service method or operation. For API calls, this should be the name of the API method. For example, 'google.datastore.v1.Datastore.RunQuery'.\n", - }, - { - name: 'num_response_items', - type: 'long', - description: - 'The number of items returned from a List or Query API method, if applicable.\n', - }, - { - name: 'request', - type: 'group', - description: 'The operation request.\n', - fields: [ - { - name: 'proto_name', - type: 'keyword', - description: 'Type property of the request.\n', - }, - { - name: 'filter', - type: 'keyword', - description: 'Filter of the request.\n', - }, - { - name: 'name', - type: 'keyword', - description: 'Name of the request.\n', - }, - { - name: 'resource_name', - type: 'keyword', - description: 'Name of the request resource.\n', - }, - ], - }, - { - name: 'request_metadata', - type: 'group', - description: 'Metadata about the request.\n', - fields: [ - { - name: 'caller_ip', - type: 'ip', - description: 'The IP address of the caller.\n', - }, - { - name: 'caller_supplied_user_agent', - type: 'keyword', - description: - 'The user agent of the caller. This information is not authenticated and should be treated accordingly.\n', - }, - ], - }, - { - name: 'resource_name', - type: 'keyword', - description: - "The resource or collection that is the target of the operation. The name is a scheme-less URI, not including the API service name. For example, 'shelves/SHELF_ID/books'.\n", - }, - { - name: 'resource_location', - type: 'group', - description: 'The location of the resource.\n', - fields: [ - { - name: 'current_locations', - type: 'keyword', - description: 'Current locations of the resource.\n', - }, - ], - }, - { - name: 'service_name', - type: 'keyword', - description: - 'The name of the API service performing the operation. For example, datastore.googleapis.com.\n', - }, - { - name: 'status', - type: 'group', - description: 'The status of the overall operation.\n', - fields: [ - { - name: 'code', - type: 'integer', - description: - 'The status code, which should be an enum value of google.rpc.Code.\n', - }, - { - name: 'message', - type: 'keyword', - description: - 'A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.\n', - }, - ], - }, - ], - }, - { - name: 'firewall', - type: 'group', - description: 'Fields for Google Cloud Firewall logs.\n', - fields: [ - { - name: 'rule_details', - type: 'group', - description: 'Description of the firewall rule that matched this connection.\n', - fields: [ - { - name: 'priority', - type: 'long', - description: 'The priority for the firewall rule.', - }, - { - name: 'action', - type: 'keyword', - description: 'Action that the rule performs on match.', - }, - { - name: 'direction', - type: 'keyword', - description: 'Direction of traffic that matches this rule.', - }, - { - name: 'reference', - type: 'keyword', - description: 'Reference to the firewall rule.', - }, - { - name: 'source_range', - type: 'keyword', - description: 'List of source ranges that the firewall rule applies to.', - }, - { - name: 'destination_range', - type: 'keyword', - description: 'List of destination ranges that the firewall applies to.', - }, - { - name: 'source_tag', - type: 'keyword', - description: 'List of all the source tags that the firewall rule applies to.\n', - }, - { - name: 'target_tag', - type: 'keyword', - description: 'List of all the target tags that the firewall rule applies to.\n', - }, - { - name: 'ip_port_info', - type: 'array', - description: 'List of ip protocols and applicable port ranges for rules.\n', - }, - { - name: 'source_service_account', - type: 'keyword', - description: - 'List of all the source service accounts that the firewall rule applies to.\n', - }, - { - name: 'target_service_account', - type: 'keyword', - description: - 'List of all the target service accounts that the firewall rule applies to.\n', - }, - ], - }, - ], - }, - { - name: 'vpcflow', - type: 'group', - description: 'Fields for Google Cloud VPC flow logs.\n', - fields: [ - { - name: 'reporter', - type: 'keyword', - description: "The side which reported the flow. Can be either 'SRC' or 'DEST'.\n", - }, - { - name: 'rtt.ms', - type: 'long', - description: - 'Latency as measured (for TCP flows only) during the time interval. This is the time elapsed between sending a SEQ and receiving a corresponding ACK and it contains the network RTT as well as the application related delay.\n', - }, - ], - }, - ], - }, - ], - }, - { - key: 'ibmmq', - title: 'ibmmq', - description: 'ibmmq Module\n', - release: 'ga', - fields: [ - { - name: 'ibmmq', - type: 'group', - description: '\n', - fields: [ - { - name: 'errorlog', - description: 'IBM MQ error logs', - type: 'group', - fields: [ - { - name: 'installation', - description: - 'This is the installation name which can be given at installation time.\nEach installation of IBM MQ on UNIX, Linux, and Windows, has a unique identifier known as an installation name. The installation name is used to associate things such as queue managers and configuration files with an installation.\n', - type: 'keyword', - }, - { - name: 'qmgr', - description: - 'Name of the queue manager. Queue managers provide queuing services to applications, and manages the queues that belong to them.\n', - type: 'keyword', - }, - { - name: 'arithinsert', - description: 'Changing content based on error.id', - type: 'keyword', - }, - { - name: 'commentinsert', - description: 'Changing content based on error.id', - type: 'keyword', - }, - { - name: 'errordescription', - description: 'Please add description', - example: 'Please add example', - type: 'text', - }, - { - name: 'explanation', - description: 'Explaines the error in more detail', - type: 'keyword', - }, - { - name: 'action', - description: 'Defines what to do when the error occurs', - type: 'keyword', - }, - { - name: 'code', - description: 'Error code.', - type: 'keyword', - }, - ], - }, - ], - }, - ], - }, - { - key: 'iptables', - title: 'iptables', - description: 'Module for handling the iptables logs.\n', - fields: [ - { - name: 'iptables', - type: 'group', - description: 'Fields from the iptables logs.\n', - fields: [ - { - name: 'ether_type', - type: 'long', - description: - 'Value of the ethernet type field identifying the network layer protocol.\n', - }, - { - name: 'flow_label', - type: 'integer', - description: 'IPv6 flow label.\n', - }, - { - name: 'fragment_flags', - type: 'keyword', - description: 'IP fragment flags. A combination of CE, DF and MF.\n', - }, - { - name: 'fragment_offset', - type: 'long', - description: 'Offset of the current IP fragment.\n', - }, - { - name: 'icmp', - type: 'group', - description: 'ICMP fields.\n', - fields: [ - { - name: 'code', - type: 'long', - description: 'ICMP code.\n', - }, - { - name: 'id', - type: 'long', - description: 'ICMP ID.\n', - }, - { - name: 'parameter', - type: 'long', - description: 'ICMP parameter.\n', - }, - { - name: 'redirect', - type: 'ip', - description: 'ICMP redirect address.\n', - }, - { - name: 'seq', - type: 'long', - description: 'ICMP sequence number.\n', - }, - { - name: 'type', - type: 'long', - description: 'ICMP type.\n', - }, - ], - }, - { - name: 'id', - type: 'long', - description: 'Packet identifier.\n', - }, - { - name: 'incomplete_bytes', - type: 'long', - description: 'Number of incomplete bytes.\n', - }, - { - name: 'input_device', - type: 'keyword', - description: 'Device that received the packet.\n', - }, - { - name: 'precedence_bits', - type: 'short', - description: 'IP precedence bits.\n', - }, - { - name: 'tos', - type: 'long', - description: 'IP Type of Service field.\n', - }, - { - name: 'length', - type: 'long', - description: 'Packet length.\n', - }, - { - name: 'output_device', - type: 'keyword', - description: 'Device that output the packet.\n', - }, - { - name: 'tcp', - type: 'group', - description: 'TCP fields.\n', - fields: [ - { - name: 'flags', - type: 'keyword', - description: 'TCP flags.\n', - }, - { - name: 'reserved_bits', - type: 'short', - description: 'TCP reserved bits.\n', - }, - { - name: 'seq', - type: 'long', - description: 'TCP sequence number.\n', - }, - { - name: 'ack', - type: 'long', - description: 'TCP Acknowledgment number.\n', - }, - { - name: 'window', - type: 'long', - description: 'Advertised TCP window size.\n', - }, - ], - }, - { - name: 'ttl', - type: 'integer', - description: 'Time To Live field.\n', - }, - { - name: 'udp', - type: 'group', - description: 'UDP fields.\n', - fields: [ - { - name: 'length', - type: 'long', - description: 'Length of the UDP header and payload.\n', - }, - ], - }, - { - name: 'ubiquiti', - type: 'group', - description: 'Fields for Ubiquiti network devices.\n', - fields: [ - { - name: 'input_zone', - type: 'keyword', - description: 'Input zone.\n', - }, - { - name: 'output_zone', - type: 'keyword', - description: 'Output zone.\n', - }, - { - name: 'rule_number', - type: 'keyword', - description: 'The rule number within the rule set.', - }, - { - name: 'rule_set', - type: 'keyword', - description: 'The rule set name.', - }, - ], - }, - ], - }, - ], - }, - { - key: 'misp', - title: 'MISP', - description: 'Module for handling threat information from MISP.\n', - fields: [ - { - name: 'misp', - type: 'group', - description: 'Fields from MISP threat information.\n', - fields: [ - { - name: 'attack_pattern', - title: 'Attack Pattern', - short: 'Fields that let you store attack patterns', - description: - 'Fields provide support for specifying information about attack patterns.\n', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the threat indicator.\n', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: 'Name of the attack pattern.\n', - }, - { - name: 'description', - level: 'extended', - type: 'text', - description: 'Description of the attack pattern.\n', - }, - { - name: 'kill_chain_phases', - level: 'extended', - type: 'keyword', - description: 'The kill chain phase(s) to which this attack pattern corresponds.\n', - }, - ], - }, - { - name: 'campaign', - title: 'Campaign', - short: 'Fields that let you store campaign information', - description: 'Fields provide support for specifying information about campaigns.\n', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the campaign.\n', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: 'Name of the campaign.\n', - }, - { - name: 'description', - level: 'extended', - type: 'text', - description: 'Description of the campaign.\n', - }, - { - name: 'aliases', - level: 'extended', - type: 'text', - description: 'Alternative names used to identify this campaign.\n', - }, - { - name: 'first_seen', - level: 'core', - type: 'date', - description: 'The time that this Campaign was first seen, in RFC3339 format.\n', - }, - { - name: 'last_seen', - level: 'core', - type: 'date', - description: 'The time that this Campaign was last seen, in RFC3339 format.\n', - }, - { - name: 'objective', - level: 'core', - type: 'keyword', - description: - "This field defines the Campaign's primary goal, objective, desired outcome, or intended effect.\n", - }, - ], - }, - { - name: 'course_of_action', - title: 'Course of Action', - short: 'Fields that let you store information about course of action.', - description: - 'A Course of Action is an action taken either to prevent an attack or to respond to an attack that is in progress.\n', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the Course of Action.\n', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: 'The name used to identify the Course of Action.\n', - }, - { - name: 'description', - level: 'extended', - type: 'text', - description: 'Description of the Course of Action.\n', - }, - ], - }, - { - name: 'identity', - title: 'Identity', - short: 'Fields that let you store information about identity.', - description: - 'Identity can represent actual individuals, organizations, or groups, as well as classes of individuals, organizations, or groups.\n', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the Identity.\n', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: 'The name used to identify the Identity.\n', - }, - { - name: 'description', - level: 'extended', - type: 'text', - description: 'Description of the Identity.\n', - }, - { - name: 'identity_class', - level: 'core', - type: 'keyword', - description: - 'The type of entity that this Identity describes, e.g., an individual or organization. Open Vocab - identity-class-ov\n', - }, - { - name: 'labels', - level: 'extended', - type: 'keyword', - description: 'The list of roles that this Identity performs.\n', - example: 'CEO\n', - }, - { - name: 'sectors', - level: 'extended', - type: 'keyword', - description: - 'The list of sectors that this Identity belongs to. Open Vocab - industry-sector-ov\n', - }, - { - name: 'contact_information', - level: 'extended', - type: 'text', - description: - 'The contact information (e-mail, phone number, etc.) for this Identity.\n', - }, - ], - }, - { - name: 'intrusion_set', - title: 'Intrusion Set', - short: 'Fields that let you store information about Intrusion Set.', - description: - 'An Intrusion Set is a grouped set of adversary behavior and resources with common properties that is believed to be orchestrated by a single organization.\n', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the Intrusion Set.\n', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: 'The name used to identify the Intrusion Set.\n', - }, - { - name: 'description', - level: 'extended', - type: 'text', - description: 'Description of the Intrusion Set.\n', - }, - { - name: 'aliases', - level: 'extended', - type: 'text', - description: 'Alternative names used to identify the Intrusion Set.\n', - }, - { - name: 'first_seen', - level: 'extended', - type: 'date', - description: - 'The time that this Intrusion Set was first seen, in RFC3339 format.\n', - }, - { - name: 'last_seen', - level: 'extended', - type: 'date', - description: 'The time that this Intrusion Set was last seen, in RFC3339 format.\n', - }, - { - name: 'goals', - level: 'extended', - type: 'text', - description: - 'The high level goals of this Intrusion Set, namely, what are they trying to do.\n', - }, - { - name: 'resource_level', - level: 'extended', - type: 'text', - description: - 'This defines the organizational level at which this Intrusion Set typically works. Open Vocab - attack-resource-level-ov\n', - }, - { - name: 'primary_motivation', - level: 'extended', - type: 'text', - description: - 'The primary reason, motivation, or purpose behind this Intrusion Set. Open Vocab - attack-motivation-ov\n', - }, - { - name: 'secondary_motivations', - level: 'extended', - type: 'text', - description: - 'The secondary reasons, motivations, or purposes behind this Intrusion Set. Open Vocab - attack-motivation-ov\n', - }, - ], - }, - { - name: 'malware', - title: 'Malware', - short: 'Fields that let you store information about Malware.', - description: - "Malware is a type of TTP that is also known as malicious code and malicious software, refers to a program that is inserted into a system, usually covertly, with the intent of compromising the confidentiality, integrity, or availability of the victim's data, applications, or operating system (OS) or of otherwise annoying or disrupting the victim.\n", - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the Malware.\n', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: 'The name used to identify the Malware.\n', - }, - { - name: 'description', - level: 'extended', - type: 'text', - description: 'Description of the Malware.\n', - }, - { - name: 'labels', - level: 'core', - type: 'keyword', - description: - 'The type of malware being described. Open Vocab - malware-label-ov. adware,backdoor,bot,ddos,dropper,exploit-kit,keylogger,ransomware, remote-access-trojan,resource-exploitation,rogue-security-software,rootkit, screen-capture,spyware,trojan,virus,worm\n', - }, - { - name: 'kill_chain_phases', - format: 'string', - level: 'extended', - type: 'keyword', - description: - 'The list of kill chain phases for which this Malware instance can be used.\n', - }, - ], - }, - { - name: 'note', - title: 'Note', - short: 'Fields that let you store information about Malware.', - description: - 'A Note is a comment or note containing informative text to help explain the context of one or more STIX Objects (SDOs or SROs) or to provide additional analysis that is not contained in the original object.\n', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the Note.\n', - }, - { - name: 'summary', - level: 'extended', - type: 'keyword', - description: 'A brief description used as a summary of the Note.\n', - }, - { - name: 'description', - level: 'extended', - type: 'text', - description: 'The content of the Note.\n', - }, - { - name: 'authors', - level: 'extended', - type: 'keyword', - description: 'The name of the author(s) of this Note.\n', - }, - { - name: 'object_refs', - level: 'extended', - type: 'keyword', - description: - 'The STIX Objects (SDOs and SROs) that the note is being applied to.\n', - }, - ], - }, - { - name: 'threat_indicator', - title: 'Threat Indicator', - short: 'Fields that let you store Threat Indicators', - description: - 'Fields provide support for specifying information about threat indicators, and related matching patterns.\n', - type: 'group', - fields: [ - { - name: 'labels', - level: 'core', - type: 'keyword', - description: 'list of type open-vocab that specifies the type of indicator.\n', - example: 'Domain Watchlist\n', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the threat indicator.\n', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - description: 'Version of the threat indicator.\n', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - description: 'Type of the threat indicator.\n', - }, - { - name: 'description', - level: 'core', - type: 'text', - description: 'Description of the threat indicator.\n', - }, - { - name: 'feed', - level: 'core', - type: 'text', - description: 'Name of the threat feed.\n', - }, - { - name: 'valid_from', - level: 'core', - type: 'date', - description: - 'The time from which this Indicator should be considered valuable intelligence, in RFC3339 format.\n', - }, - { - name: 'valid_until', - level: 'core', - type: 'date', - description: - 'The time at which this Indicator should no longer be considered valuable intelligence. If the valid_until property is omitted, then there is no constraint on the latest time for which the indicator should be used, in RFC3339 format.\n', - }, - { - name: 'severity', - format: 'string', - level: 'core', - type: 'keyword', - description: 'Threat severity to which this indicator corresponds.\n', - example: 'high', - }, - { - name: 'confidence', - level: 'core', - type: 'keyword', - description: 'Confidence level to which this indicator corresponds.\n', - example: 'high', - }, - { - name: 'kill_chain_phases', - format: 'string', - level: 'extended', - type: 'keyword', - description: 'The kill chain phase(s) to which this indicator corresponds.\n', - }, - { - name: 'mitre_tactic', - format: 'string', - level: 'extended', - type: 'keyword', - description: 'MITRE tactics to which this indicator corresponds.\n', - example: 'Initial Access', - }, - { - name: 'mitre_technique', - format: 'string', - level: 'extended', - type: 'keyword', - description: 'MITRE techniques to which this indicator corresponds.\n', - example: 'Drive-by Compromise', - }, - { - name: 'attack_pattern', - level: 'core', - type: 'keyword', - description: - 'The attack_pattern for this indicator is a STIX Pattern as specified in STIX Version 2.0 Part 5 - STIX Patterning.\n', - example: "[destination:ip = '91.219.29.188/32']\n", - }, - { - name: 'attack_pattern_kql', - level: 'core', - type: 'keyword', - description: - 'The attack_pattern for this indicator is KQL query that matches the attack_pattern specified in the STIX Pattern format.\n', - example: 'destination.ip: "91.219.29.188/32"\n', - }, - { - name: 'negate', - level: 'core', - type: 'boolean', - description: 'When set to true, it specifies the absence of the attack_pattern.\n', - }, - { - name: 'intrusion_set', - level: 'extended', - type: 'keyword', - description: 'Name of the intrusion set if known.\n', - }, - { - name: 'campaign', - level: 'extended', - type: 'keyword', - description: 'Name of the attack campaign if known.\n', - }, - { - name: 'threat_actor', - level: 'extended', - type: 'keyword', - description: 'Name of the threat actor if known.\n', - }, - ], - }, - { - name: 'observed_data', - title: 'Observed Data', - short: 'Fields that let you store information about Observed Data.', - description: - 'Observed data conveys information that was observed on systems and networks, such as log data or network traffic, using the Cyber Observable specification.\n', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the Observed Data.\n', - }, - { - name: 'first_observed', - level: 'core', - type: 'date', - description: - 'The beginning of the time window that the data was observed, in RFC3339 format.\n', - }, - { - name: 'last_observed', - level: 'core', - type: 'date', - description: - 'The end of the time window that the data was observed, in RFC3339 format.\n', - }, - { - name: 'number_observed', - level: 'core', - type: 'integer', - description: - 'The number of times the data represented in the objects property was observed. This MUST be an integer between 1 and 999,999,999 inclusive.\n', - }, - { - name: 'objects', - level: 'core', - type: 'keyword', - description: - 'A dictionary of Cyber Observable Objects that describes the single fact that was observed.\n', - }, - ], - }, - { - name: 'report', - title: 'Report', - short: 'Fields that let you store information about Report.', - description: - 'Reports are collections of threat intelligence focused on one or more topics, such as a description of a threat actor, malware, or attack technique, including context and related details.\n', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the Report.\n', - }, - { - name: 'labels', - level: 'core', - type: 'keyword', - description: - 'This field is an Open Vocabulary that specifies the primary subject of this report. Open Vocab - report-label-ov. threat-report,attack-pattern,campaign,identity,indicator,malware,observed-data,threat-actor,tool,vulnerability\n', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: 'The name used to identify the Report.\n', - }, - { - name: 'description', - level: 'extended', - type: 'text', - description: 'A description that provides more details and context about Report.\n', - }, - { - name: 'published', - level: 'extended', - type: 'date', - description: - 'The date that this report object was officially published by the creator of this report, in RFC3339 format.\n', - }, - { - name: 'object_refs', - level: 'core', - type: 'text', - description: 'Specifies the STIX Objects that are referred to by this Report.\n', - }, - ], - }, - { - name: 'threat_actor', - title: 'Threat Actor', - short: 'Fields that let you store information about Threat Actor.', - description: - 'Threat Actors are actual individuals, groups, or organizations believed to be operating with malicious intent.\n', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the Threat Actor.\n', - }, - { - name: 'labels', - level: 'core', - type: 'keyword', - description: - 'This field specifies the type of threat actor. Open Vocab - threat-actor-label-ov. activist,competitor,crime-syndicate,criminal,hacker,insider-accidental,insider-disgruntled,nation-state,sensationalist,spy,terrorist\n', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: 'The name used to identify this Threat Actor or Threat Actor group.\n', - }, - { - name: 'description', - level: 'extended', - type: 'text', - description: - 'A description that provides more details and context about the Threat Actor.\n', - }, - { - name: 'aliases', - level: 'extended', - type: 'text', - description: 'A list of other names that this Threat Actor is believed to use.\n', - }, - { - name: 'roles', - level: 'extended', - type: 'text', - description: - 'This is a list of roles the Threat Actor plays. Open Vocab - threat-actor-role-ov. agent,director,independent,sponsor,infrastructure-operator,infrastructure-architect,malware-author\n', - }, - { - name: 'goals', - level: 'extended', - type: 'text', - description: - 'The high level goals of this Threat Actor, namely, what are they trying to do.\n', - }, - { - name: 'sophistication', - level: 'extended', - type: 'text', - description: - 'The skill, specific knowledge, special training, or expertise a Threat Actor must have to perform the attack. Open Vocab - threat-actor-sophistication-ov. none,minimal,intermediate,advanced,strategic,expert,innovator\n', - }, - { - name: 'resource_level', - level: 'extended', - type: 'text', - description: - 'This defines the organizational level at which this Threat Actor typically works. Open Vocab - attack-resource-level-ov. individual,club,contest,team,organization,government\n', - }, - { - name: 'primary_motivation', - level: 'extended', - type: 'text', - description: - 'The primary reason, motivation, or purpose behind this Threat Actor. Open Vocab - attack-motivation-ov. accidental,coercion,dominance,ideology,notoriety,organizational-gain,personal-gain,personal-satisfaction,revenge,unpredictable\n', - }, - { - name: 'secondary_motivations', - level: 'extended', - type: 'text', - description: - 'The secondary reasons, motivations, or purposes behind this Threat Actor. Open Vocab - attack-motivation-ov. accidental,coercion,dominance,ideology,notoriety,organizational-gain,personal-gain,personal-satisfaction,revenge,unpredictable\n', - }, - { - name: 'personal_motivations', - level: 'extended', - type: 'text', - description: - 'The personal reasons, motivations, or purposes of the Threat Actor regardless of organizational goals. Open Vocab - attack-motivation-ov. accidental,coercion,dominance,ideology,notoriety,organizational-gain,personal-gain,personal-satisfaction,revenge,unpredictable\n', - }, - ], - }, - { - name: 'tool', - title: 'Tool', - short: 'Fields that let you store information about Tool.', - description: - 'Tools are legitimate software that can be used by threat actors to perform attacks.\n', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the Tool.\n', - }, - { - name: 'labels', - level: 'core', - type: 'keyword', - description: - 'The kind(s) of tool(s) being described. Open Vocab - tool-label-ov. denial-of-service,exploitation,information-gathering,network-capture,credential-exploitation,remote-access,vulnerability-scanning\n', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: 'The name used to identify the Tool.\n', - }, - { - name: 'description', - level: 'extended', - type: 'text', - description: - 'A description that provides more details and context about the Tool.\n', - }, - { - name: 'tool_version', - level: 'extended', - type: 'keyword', - description: 'The version identifier associated with the Tool.\n', - }, - { - name: 'kill_chain_phases', - level: 'extended', - type: 'text', - description: - 'The list of kill chain phases for which this Tool instance can be used.\n', - }, - ], - }, - { - name: 'vulnerability', - title: 'Vulnerability', - short: 'Fields that let you store information about Vulnerability.', - description: - 'A Vulnerability is a mistake in software that can be directly used by a hacker to gain access to a system or network.\n', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Identifier of the Vulnerability.\n', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: 'The name used to identify the Vulnerability.\n', - }, - { - name: 'description', - level: 'extended', - type: 'text', - description: - 'A description that provides more details and context about the Vulnerability.\n', - }, - ], - }, - ], - }, - ], - }, - { - key: 'mssql', - title: 'mssql', - description: 'MS SQL Filebeat Module', - fields: [ - { - name: 'mssql', - type: 'group', - description: 'Fields from the MSSQL log files', - fields: [ - { - name: 'log', - description: 'Common log fields', - type: 'group', - fields: [ - { - name: 'origin', - description: - 'Origin of the message, usually the server but it can also be a recovery process', - type: 'keyword', - }, - ], - }, - ], - }, - ], - }, - { - key: 'netflow-module', - title: 'NetFlow', - description: - 'Module for receiving NetFlow and IPFIX flow records over UDP. The module does not add fields beyond what the netflow input provides.\n', - fields: [], - }, - { - key: 'o365', - title: 'Office 365', - description: 'Module for handling logs from Office 365.\n', - fields: [ - { - name: 'o365.audit', - type: 'group', - default_field: false, - description: 'Fields from Office 365 Management API audit logs.\n', - fields: [ - { - name: 'Actor', - type: 'array', - fields: [ - { - name: 'ID', - type: 'keyword', - }, - { - name: 'Type', - type: 'keyword', - }, - ], - }, - { - name: 'ActorContextId', - type: 'keyword', - }, - { - name: 'ActorIpAddress', - type: 'keyword', - }, - { - name: 'ActorUserId', - type: 'keyword', - }, - { - name: 'ActorYammerUserId', - type: 'keyword', - }, - { - name: 'AlertEntityId', - type: 'keyword', - }, - { - name: 'AlertId', - type: 'keyword', - }, - { - name: 'AlertLinks', - type: 'array', - }, - { - name: 'AlertType', - type: 'keyword', - }, - { - name: 'AppId', - type: 'keyword', - }, - { - name: 'ApplicationDisplayName', - type: 'keyword', - }, - { - name: 'ApplicationId', - type: 'keyword', - }, - { - name: 'AzureActiveDirectoryEventType', - type: 'keyword', - }, - { - name: 'ExchangeMetaData.*', - type: 'object', - }, - { - name: 'Category', - type: 'keyword', - }, - { - name: 'ClientAppId', - type: 'keyword', - }, - { - name: 'ClientInfoString', - type: 'keyword', - }, - { - name: 'ClientIP', - type: 'keyword', - }, - { - name: 'ClientIPAddress', - type: 'keyword', - }, - { - name: 'Comments', - type: 'text', - norms: false, - }, - { - name: 'CorrelationId', - type: 'keyword', - }, - { - name: 'CreationTime', - type: 'keyword', - }, - { - name: 'CustomUniqueId', - type: 'keyword', - }, - { - name: 'Data', - type: 'keyword', - }, - { - name: 'DataType', - type: 'keyword', - }, - { - name: 'EntityType', - type: 'keyword', - }, - { - name: 'EventData', - type: 'keyword', - }, - { - name: 'EventSource', - type: 'keyword', - }, - { - name: 'ExceptionInfo.*', - type: 'object', - }, - { - name: 'ExtendedProperties.*', - type: 'object', - }, - { - name: 'ExternalAccess', - type: 'keyword', - }, - { - name: 'GroupName', - type: 'keyword', - }, - { - name: 'Id', - type: 'keyword', - }, - { - name: 'ImplicitShare', - type: 'keyword', - }, - { - name: 'IncidentId', - type: 'keyword', - }, - { - name: 'InternalLogonType', - type: 'keyword', - }, - { - name: 'InterSystemsId', - type: 'keyword', - }, - { - name: 'IntraSystemId', - type: 'keyword', - }, - { - name: 'Item.*', - type: 'object', - }, - { - name: 'Item.*.*', - type: 'object', - }, - { - name: 'ItemName', - type: 'keyword', - }, - { - name: 'ItemType', - type: 'keyword', - }, - { - name: 'ListId', - type: 'keyword', - }, - { - name: 'ListItemUniqueId', - type: 'keyword', - }, - { - name: 'LogonError', - type: 'keyword', - }, - { - name: 'LogonType', - type: 'keyword', - }, - { - name: 'LogonUserSid', - type: 'keyword', - }, - { - name: 'MailboxGuid', - type: 'keyword', - }, - { - name: 'MailboxOwnerMasterAccountSid', - type: 'keyword', - }, - { - name: 'MailboxOwnerSid', - type: 'keyword', - }, - { - name: 'MailboxOwnerUPN', - type: 'keyword', - }, - { - name: 'Members', - type: 'array', - }, - { - name: 'Members.*', - type: 'object', - }, - { - name: 'ModifiedProperties.*.*', - type: 'object', - }, - { - name: 'Name', - type: 'keyword', - }, - { - name: 'ObjectId', - type: 'keyword', - }, - { - name: 'Operation', - type: 'keyword', - }, - { - name: 'OrganizationId', - type: 'keyword', - }, - { - name: 'OrganizationName', - type: 'keyword', - }, - { - name: 'OriginatingServer', - type: 'keyword', - }, - { - name: 'Parameters.*', - type: 'object', - }, - { - name: 'PolicyDetails', - type: 'array', - }, - { - name: 'PolicyId', - type: 'keyword', - }, - { - name: 'RecordType', - type: 'keyword', - }, - { - name: 'ResultStatus', - type: 'keyword', - }, - { - name: 'SensitiveInfoDetectionIsIncluded', - type: 'keyword', - }, - { - name: 'SharePointMetaData.*', - type: 'object', - }, - { - name: 'SessionId', - type: 'keyword', - }, - { - name: 'Severity', - type: 'keyword', - }, - { - name: 'Site', - type: 'keyword', - }, - { - name: 'SiteUrl', - type: 'keyword', - }, - { - name: 'Source', - type: 'keyword', - }, - { - name: 'SourceFileExtension', - type: 'keyword', - }, - { - name: 'SourceFileName', - type: 'keyword', - }, - { - name: 'SourceRelativeUrl', - type: 'keyword', - }, - { - name: 'Status', - type: 'keyword', - }, - { - name: 'SupportTicketId', - type: 'keyword', - }, - { - name: 'Target', - type: 'array', - fields: [ - { - name: 'ID', - type: 'keyword', - }, - { - name: 'Type', - type: 'keyword', - }, - ], - }, - { - name: 'TargetContextId', - type: 'keyword', - }, - { - name: 'TargetUserOrGroupName', - type: 'keyword', - }, - { - name: 'TargetUserOrGroupType', - type: 'keyword', - }, - { - name: 'TeamName', - type: 'keyword', - }, - { - name: 'TeamGuid', - type: 'keyword', - }, - { - name: 'UniqueSharingId', - type: 'keyword', - }, - { - name: 'UserAgent', - type: 'keyword', - }, - { - name: 'UserId', - type: 'keyword', - }, - { - name: 'UserKey', - type: 'keyword', - }, - { - name: 'UserType', - type: 'keyword', - }, - { - name: 'Version', - type: 'keyword', - }, - { - name: 'WebId', - type: 'keyword', - }, - { - name: 'Workload', - type: 'keyword', - }, - { - name: 'YammerNetworkId', - type: 'keyword', - }, - ], - }, - ], - }, - { - key: 'okta', - title: 'Okta', - description: 'Module for handling system logs from Okta.\n', - fields: [ - { - name: 'okta', - type: 'group', - default_field: false, - description: 'Fields from Okta.\n', - fields: [ - { - name: 'uuid', - title: 'UUID', - short: 'The unique identifier of the Okta LogEvent.', - description: 'The unique identifier of the Okta LogEvent.\n', - type: 'keyword', - }, - { - name: 'event_type', - title: 'Event Type', - short: 'The type of the LogEvent.', - description: 'The type of the LogEvent.\n', - type: 'keyword', - }, - { - name: 'version', - title: 'Version', - short: 'The version of the LogEvent.', - description: 'The version of the LogEvent.\n', - type: 'keyword', - }, - { - name: 'severity', - title: 'Severity', - short: 'The severity of the LogEvent.', - description: - 'The severity of the LogEvent. Must be one of DEBUG, INFO, WARN, or ERROR.\n', - type: 'keyword', - }, - { - name: 'display_message', - title: 'Display Message', - short: 'The display message of the LogEvent.', - description: 'The display message of the LogEvent.\n', - type: 'keyword', - }, - { - name: 'actor', - title: 'Actor', - short: 'Fields of the actor for the LogEvent.', - description: 'Fields that let you store information of the actor for the LogEvent.\n', - type: 'group', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'Identifier of the actor.\n', - }, - { - name: 'type', - type: 'keyword', - description: 'Type of the actor.\n', - }, - { - name: 'alternate_id', - type: 'keyword', - description: 'Alternate identifier of the actor.\n', - }, - { - name: 'display_name', - type: 'keyword', - description: 'Display name of the actor.\n', - }, - ], - }, - { - name: 'client', - title: 'Client', - short: 'Fields about the client of the actor.', - description: 'Fields that let you store information about the client of the actor.\n', - type: 'group', - fields: [ - { - name: 'ip', - type: 'ip', - description: 'The IP address of the client.\n', - }, - { - name: 'user_agent', - description: 'Fields about the user agent information of the client.\n', - type: 'group', - fields: [ - { - name: 'raw_user_agent', - type: 'keyword', - description: 'The raw informaton of the user agent.\n', - }, - { - name: 'os', - type: 'keyword', - description: 'The OS informaton.\n', - }, - { - name: 'browser', - type: 'keyword', - description: 'The browser informaton of the client.\n', - }, - ], - }, - { - name: 'zone', - type: 'keyword', - description: 'The zone information of the client.\n', - }, - { - name: 'device', - type: 'keyword', - description: 'The information of the client device.\n', - }, - { - name: 'id', - type: 'keyword', - description: 'The identifier of the client.\n', - }, - ], - }, - { - name: 'outcome', - title: 'Outcome of the LogEvent.', - short: 'Fields that let you store information about the outcome.', - description: 'Fields that let you store information about the outcome.\n', - type: 'group', - fields: [ - { - name: 'reason', - type: 'keyword', - description: 'The reason of the outcome.\n', - }, - { - name: 'result', - type: 'keyword', - description: - 'The result of the outcome. Must be one of: SUCCESS, FAILURE, SKIPPED, ALLOW, DENY, CHALLENGE, UNKNOWN.\n', - }, - ], - }, - { - name: 'target', - title: 'Target', - short: 'The list of targets.', - description: 'The list of targets.\n', - type: 'array', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'Identifier of the actor.\n', - }, - { - name: 'type', - type: 'keyword', - description: 'Type of the actor.\n', - }, - { - name: 'alternate_id', - type: 'keyword', - description: 'Alternate identifier of the actor.\n', - }, - { - name: 'display_name', - type: 'keyword', - description: 'Display name of the actor.\n', - }, - ], - }, - { - name: 'transaction', - title: 'Transaction', - short: 'Fields that let you store information about related transaction.', - description: 'Fields that let you store information about related transaction.\n', - type: 'group', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'Identifier of the transaction.\n', - }, - { - name: 'type', - type: 'keyword', - description: 'The type of transaction. Must be one of "WEB", "JOB".\n', - }, - ], - }, - { - name: 'debug_context', - title: 'Debug Context', - short: 'Fields that let you store information about the debug context.', - description: 'Fields that let you store information about the debug context.\n', - type: 'group', - fields: [ - { - name: 'debug_data', - description: 'The debug data.\n', - type: 'group', - fields: [ - { - name: 'device_fingerprint', - type: 'keyword', - description: 'The fingerprint of the device.\n', - }, - { - name: 'request_id', - type: 'keyword', - description: 'The identifier of the request.\n', - }, - { - name: 'request_uri', - type: 'keyword', - description: 'The request URI.\n', - }, - { - name: 'threat_suspected', - type: 'keyword', - description: 'Threat suspected.\n', - }, - { - name: 'url', - type: 'keyword', - description: 'The URL.\n', - }, - ], - }, - ], - }, - { - name: 'authentication_context', - title: 'Authentication Context', - short: 'Fields that let you store information about authentication context.', - description: 'Fields that let you store information about authentication context.\n', - type: 'group', - fields: [ - { - name: 'authentication_provider', - type: 'keyword', - description: - 'The information about the authentication provider. Must be one of OKTA_AUTHENTICATION_PROVIDER, ACTIVE_DIRECTORY, LDAP, FEDERATION, SOCIAL, FACTOR_PROVIDER.\n', - }, - { - name: 'authentication_step', - type: 'integer', - description: 'The authentication step.\n', - }, - { - name: 'credential_provider', - type: 'keyword', - description: - 'The information about credential provider. Must be one of OKTA_CREDENTIAL_PROVIDER, RSA, SYMANTEC, GOOGLE, DUO, YUBIKEY.\n', - }, - { - name: 'credential_type', - type: 'keyword', - description: - 'The information about credential type. Must be one of OTP, SMS, PASSWORD, ASSERTION, IWA, EMAIL, OAUTH2, JWT, CERTIFICATE, PRE_SHARED_SYMMETRIC_KEY, OKTA_CLIENT_SESSION, DEVICE_UDID.\n', - }, - { - name: 'issuer', - description: 'The information about the issuer.\n', - type: 'array', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'The identifier of the issuer.\n', - }, - { - name: 'type', - type: 'keyword', - description: 'The type of the issuer.\n', - }, - ], - }, - { - name: 'external_session_id', - type: 'keyword', - description: 'The session identifer of the external session if any.\n', - }, - { - name: 'interface', - type: 'keyword', - description: 'The interface used. e.g., Outlook, Office365, wsTrust\n', - }, - ], - }, - { - name: 'security_context', - title: 'Security Context', - short: 'Fields that let you store information about security context.', - description: 'Fields that let you store information about security context.\n', - type: 'group', - fields: [ - { - name: 'as', - type: 'group', - description: 'The autonomous system.\n', - fields: [ - { - name: 'number', - type: 'integer', - description: 'The AS number.\n', - }, - { - name: 'organization', - type: 'group', - description: 'The organization that owns the AS number.\n', - fields: [ - { - name: 'name', - type: 'keyword', - description: 'The organization name.\n', - }, - ], - }, - ], - }, - { - name: 'isp', - type: 'keyword', - description: 'The Internet Service Provider.\n', - }, - { - name: 'domain', - type: 'keyword', - description: 'The domain name.\n', - }, - { - name: 'is_proxy', - type: 'boolean', - description: 'Whether it is a proxy or not.\n', - }, - ], - }, - { - name: 'request', - title: 'Request', - short: 'Fields that let you store information about the request.', - description: - 'Fields that let you store information about the request, in the form of list of ip_chain.\n', - type: 'group', - fields: [ - { - name: 'ip_chain', - description: 'List of ip_chain objects.\n', - type: 'group', - fields: [ - { - name: 'ip', - type: 'ip', - description: 'IP address.\n', - }, - { - name: 'version', - type: 'keyword', - description: 'IP version. Must be one of V4, V6.\n', - }, - { - name: 'source', - type: 'keyword', - description: 'Source information.\n', - }, - { - name: 'geographical_context', - description: 'Geographical information.\n', - type: 'group', - fields: [ - { - name: 'city', - type: 'keyword', - description: 'The city.', - }, - { - name: 'state', - type: 'keyword', - description: 'The state.', - }, - { - name: 'postal_code', - type: 'keyword', - description: 'The postal code.', - }, - { - name: 'country', - type: 'keyword', - description: 'The country.', - }, - { - name: 'geolocation', - description: 'Geolocation information.\n', - type: 'geo_point', - }, - ], - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - key: 'panw', - title: 'panw', - description: 'Module for Palo Alto Networks (PAN-OS)\n', - fields: [ - { - name: 'panw', - type: 'group', - description: 'Fields from the panw module.\n', - fields: [ - { - name: 'panos', - type: 'group', - description: 'Fields for the Palo Alto Networks PAN-OS logs.\n', - fields: [ - { - name: 'ruleset', - type: 'keyword', - description: 'Name of the rule that matched this session.\n', - }, - { - name: 'source', - type: 'group', - description: 'Fields to extend the top-level source object.\n', - fields: [ - { - name: 'zone', - type: 'keyword', - description: 'Source zone for this session.\n', - }, - { - name: 'interface', - type: 'keyword', - description: 'Source interface for this session.\n', - }, - { - name: 'nat', - type: 'group', - description: 'Post-NAT source address, if source NAT is performed.\n', - fields: [ - { - name: 'ip', - type: 'ip', - description: 'Post-NAT source IP.\n', - }, - { - name: 'port', - type: 'long', - description: 'Post-NAT source port.\n', - }, - ], - }, - ], - }, - { - name: 'destination', - type: 'group', - description: 'Fields to extend the top-level destination object.\n', - fields: [ - { - name: 'zone', - type: 'keyword', - description: 'Destination zone for this session.\n', - }, - { - name: 'interface', - type: 'keyword', - description: 'Destination interface for this session.\n', - }, - { - name: 'nat', - type: 'group', - description: 'Post-NAT destination address, if destination NAT is performed.\n', - fields: [ - { - name: 'ip', - type: 'ip', - description: 'Post-NAT destination IP.\n', - }, - { - name: 'port', - type: 'long', - description: 'Post-NAT destination port.\n', - }, - ], - }, - ], - }, - { - name: 'network', - type: 'group', - description: 'Fields to extend the top-level network object.\n', - fields: [ - { - name: 'pcap_id', - type: 'keyword', - description: 'Packet capture ID for a threat.\n', - }, - { - name: 'nat', - type: 'group', - fields: [ - { - name: 'community_id', - type: 'keyword', - description: 'Community ID flow-hash for the NAT 5-tuple.\n', - }, - ], - }, - ], - }, - { - name: 'file', - type: 'group', - description: 'Fields to extend the top-level file object.\n', - fields: [ - { - name: 'hash', - description: - 'Binary hash for a threat file sent to be analyzed by the WildFire service.\n', - type: 'keyword', - }, - ], - }, - { - name: 'url', - type: 'group', - description: 'Fields to extend the top-level url object.\n', - fields: [ - { - name: 'category', - type: 'keyword', - description: - "For threat URLs, it's the URL category. For WildFire, the verdict on the file and is either 'malicious', 'grayware', or 'benign'.\n", - }, - ], - }, - { - name: 'flow_id', - type: 'keyword', - description: 'Internal numeric identifier for each session.\n', - }, - { - name: 'sequence_number', - type: 'long', - description: - 'Log entry identifier that is incremented sequentially. Unique for each log type.\n', - }, - { - name: 'threat.resource', - type: 'keyword', - description: 'URL or file name for a threat.\n', - }, - { - name: 'threat.id', - type: 'keyword', - description: 'Palo Alto Networks identifier for the threat.\n', - }, - { - name: 'threat.name', - type: 'keyword', - description: 'Palo Alto Networks name for the threat.\n', - }, - ], - }, - ], - }, - ], - }, - { - key: 'rabbitmq', - title: 'RabbitMQ', - description: 'RabbitMQ Module\n', - fields: [ - { - name: 'rabbitmq', - type: 'group', - description: '\n', - fields: [ - { - name: 'log', - type: 'group', - description: 'RabbitMQ log files\n', - fields: [ - { - name: 'pid', - type: 'keyword', - description: 'The Erlang process id', - example: '<0.222.0>', - }, - ], - }, - ], - }, - ], - }, - { - key: 'suricata', - title: 'Suricata', - description: 'Module for handling the EVE JSON logs produced by Suricata.\n', - fields: [ - { - name: 'suricata', - type: 'group', - description: 'Fields from the Suricata EVE log file.\n', - fields: [ - { - name: 'eve', - type: 'group', - description: 'Fields exported by the EVE JSON logs\n', - fields: [ - { - name: 'event_type', - type: 'keyword', - }, - { - name: 'app_proto_orig', - type: 'keyword', - }, - { - name: 'tcp', - type: 'group', - fields: [ - { - name: 'tcp_flags', - type: 'keyword', - }, - { - name: 'psh', - type: 'boolean', - }, - { - name: 'tcp_flags_tc', - type: 'keyword', - }, - { - name: 'ack', - type: 'boolean', - }, - { - name: 'syn', - type: 'boolean', - }, - { - name: 'state', - type: 'keyword', - }, - { - name: 'tcp_flags_ts', - type: 'keyword', - }, - { - name: 'rst', - type: 'boolean', - }, - { - name: 'fin', - type: 'boolean', - }, - ], - }, - { - name: 'fileinfo', - type: 'group', - fields: [ - { - name: 'sha1', - type: 'keyword', - }, - { - name: 'filename', - type: 'alias', - path: 'file.path', - }, - { - name: 'tx_id', - type: 'long', - }, - { - name: 'state', - type: 'keyword', - }, - { - name: 'stored', - type: 'boolean', - }, - { - name: 'gaps', - type: 'boolean', - }, - { - name: 'sha256', - type: 'keyword', - }, - { - name: 'md5', - type: 'keyword', - }, - { - name: 'size', - type: 'alias', - path: 'file.size', - }, - ], - }, - { - name: 'icmp_type', - type: 'long', - }, - { - name: 'dest_port', - type: 'alias', - path: 'destination.port', - }, - { - name: 'src_port', - type: 'alias', - path: 'source.port', - }, - { - name: 'proto', - type: 'alias', - path: 'network.transport', - }, - { - name: 'pcap_cnt', - type: 'long', - }, - { - name: 'src_ip', - type: 'alias', - path: 'source.ip', - }, - { - name: 'dns', - type: 'group', - fields: [ - { - name: 'type', - type: 'keyword', - }, - { - name: 'rrtype', - type: 'keyword', - }, - { - name: 'rrname', - type: 'keyword', - }, - { - name: 'rdata', - type: 'keyword', - }, - { - name: 'tx_id', - type: 'long', - }, - { - name: 'ttl', - type: 'long', - }, - { - name: 'rcode', - type: 'keyword', - }, - { - name: 'id', - type: 'long', - }, - ], - }, - { - name: 'flow_id', - type: 'keyword', - }, - { - name: 'email', - type: 'group', - fields: [ - { - name: 'status', - type: 'keyword', - }, - ], - }, - { - name: 'dest_ip', - type: 'alias', - path: 'destination.ip', - }, - { - name: 'icmp_code', - type: 'long', - }, - { - name: 'http', - type: 'group', - fields: [ - { - name: 'status', - type: 'alias', - path: 'http.response.status_code', - }, - { - name: 'redirect', - type: 'keyword', - }, - { - name: 'http_user_agent', - type: 'alias', - path: 'user_agent.original', - }, - { - name: 'protocol', - type: 'keyword', - }, - { - name: 'http_refer', - type: 'alias', - path: 'http.request.referrer', - }, - { - name: 'url', - type: 'alias', - path: 'url.original', - }, - { - name: 'hostname', - type: 'alias', - path: 'url.domain', - }, - { - name: 'length', - type: 'alias', - path: 'http.response.body.bytes', - }, - { - name: 'http_method', - type: 'alias', - path: 'http.request.method', - }, - { - name: 'http_content_type', - type: 'keyword', - }, - ], - }, - { - name: 'timestamp', - type: 'alias', - path: '@timestamp', - }, - { - name: 'in_iface', - type: 'keyword', - }, - { - name: 'alert', - type: 'group', - fields: [ - { - name: 'category', - type: 'keyword', - }, - { - name: 'severity', - type: 'alias', - path: 'event.severity', - }, - { - name: 'rev', - type: 'long', - }, - { - name: 'gid', - type: 'long', - }, - { - name: 'signature', - type: 'keyword', - }, - { - name: 'action', - type: 'alias', - path: 'event.outcome', - }, - { - name: 'signature_id', - type: 'long', - }, - ], - }, - { - name: 'ssh', - type: 'group', - fields: [ - { - name: 'client', - type: 'group', - fields: [ - { - name: 'proto_version', - type: 'keyword', - }, - { - name: 'software_version', - type: 'keyword', - }, - ], - }, - { - name: 'server', - type: 'group', - fields: [ - { - name: 'proto_version', - type: 'keyword', - }, - { - name: 'software_version', - type: 'keyword', - }, - ], - }, - ], - }, - { - name: 'stats', - type: 'group', - fields: [ - { - name: 'capture', - type: 'group', - fields: [ - { - name: 'kernel_packets', - type: 'long', - }, - { - name: 'kernel_drops', - type: 'long', - }, - { - name: 'kernel_ifdrops', - type: 'long', - }, - ], - }, - { - name: 'uptime', - type: 'long', - }, - { - name: 'detect', - type: 'group', - fields: [ - { - name: 'alert', - type: 'long', - }, - ], - }, - { - name: 'http', - type: 'group', - fields: [ - { - name: 'memcap', - type: 'long', - }, - { - name: 'memuse', - type: 'long', - }, - ], - }, - { - name: 'file_store', - type: 'group', - fields: [ - { - name: 'open_files', - type: 'long', - }, - ], - }, - { - name: 'defrag', - type: 'group', - fields: [ - { - name: 'max_frag_hits', - type: 'long', - }, - { - name: 'ipv4', - type: 'group', - fields: [ - { - name: 'timeouts', - type: 'long', - }, - { - name: 'fragments', - type: 'long', - }, - { - name: 'reassembled', - type: 'long', - }, - ], - }, - { - name: 'ipv6', - type: 'group', - fields: [ - { - name: 'timeouts', - type: 'long', - }, - { - name: 'fragments', - type: 'long', - }, - { - name: 'reassembled', - type: 'long', - }, - ], - }, - ], - }, - { - name: 'flow', - type: 'group', - fields: [ - { - name: 'tcp_reuse', - type: 'long', - }, - { - name: 'udp', - type: 'long', - }, - { - name: 'memcap', - type: 'long', - }, - { - name: 'emerg_mode_entered', - type: 'long', - }, - { - name: 'emerg_mode_over', - type: 'long', - }, - { - name: 'tcp', - type: 'long', - }, - { - name: 'icmpv6', - type: 'long', - }, - { - name: 'icmpv4', - type: 'long', - }, - { - name: 'spare', - type: 'long', - }, - { - name: 'memuse', - type: 'long', - }, - ], - }, - { - name: 'tcp', - type: 'group', - fields: [ - { - name: 'pseudo_failed', - type: 'long', - }, - { - name: 'ssn_memcap_drop', - type: 'long', - }, - { - name: 'insert_data_overlap_fail', - type: 'long', - }, - { - name: 'sessions', - type: 'long', - }, - { - name: 'pseudo', - type: 'long', - }, - { - name: 'synack', - type: 'long', - }, - { - name: 'insert_data_normal_fail', - type: 'long', - }, - { - name: 'syn', - type: 'long', - }, - { - name: 'memuse', - type: 'long', - }, - { - name: 'invalid_checksum', - type: 'long', - }, - { - name: 'segment_memcap_drop', - type: 'long', - }, - { - name: 'overlap', - type: 'long', - }, - { - name: 'insert_list_fail', - type: 'long', - }, - { - name: 'rst', - type: 'long', - }, - { - name: 'stream_depth_reached', - type: 'long', - }, - { - name: 'reassembly_memuse', - type: 'long', - }, - { - name: 'reassembly_gap', - type: 'long', - }, - { - name: 'overlap_diff_data', - type: 'long', - }, - { - name: 'no_flow', - type: 'long', - }, - ], - }, - { - name: 'decoder', - type: 'group', - fields: [ - { - name: 'avg_pkt_size', - type: 'long', - }, - { - name: 'bytes', - type: 'long', - }, - { - name: 'tcp', - type: 'long', - }, - { - name: 'raw', - type: 'long', - }, - { - name: 'ppp', - type: 'long', - }, - { - name: 'vlan_qinq', - type: 'long', - }, - { - name: 'null', - type: 'long', - }, - { - name: 'ltnull', - type: 'group', - fields: [ - { - name: 'unsupported_type', - type: 'long', - }, - { - name: 'pkt_too_small', - type: 'long', - }, - ], - }, - { - name: 'invalid', - type: 'long', - }, - { - name: 'gre', - type: 'long', - }, - { - name: 'ipv4', - type: 'long', - }, - { - name: 'ipv6', - type: 'long', - }, - { - name: 'pkts', - type: 'long', - }, - { - name: 'ipv6_in_ipv6', - type: 'long', - }, - { - name: 'ipraw', - type: 'group', - fields: [ - { - name: 'invalid_ip_version', - type: 'long', - }, - ], - }, - { - name: 'pppoe', - type: 'long', - }, - { - name: 'udp', - type: 'long', - }, - { - name: 'dce', - type: 'group', - fields: [ - { - name: 'pkt_too_small', - type: 'long', - }, - ], - }, - { - name: 'vlan', - type: 'long', - }, - { - name: 'sctp', - type: 'long', - }, - { - name: 'max_pkt_size', - type: 'long', - }, - { - name: 'teredo', - type: 'long', - }, - { - name: 'mpls', - type: 'long', - }, - { - name: 'sll', - type: 'long', - }, - { - name: 'icmpv6', - type: 'long', - }, - { - name: 'icmpv4', - type: 'long', - }, - { - name: 'erspan', - type: 'long', - }, - { - name: 'ethernet', - type: 'long', - }, - { - name: 'ipv4_in_ipv6', - type: 'long', - }, - { - name: 'ieee8021ah', - type: 'long', - }, - ], - }, - { - name: 'dns', - type: 'group', - fields: [ - { - name: 'memcap_global', - type: 'long', - }, - { - name: 'memcap_state', - type: 'long', - }, - { - name: 'memuse', - type: 'long', - }, - ], - }, - { - name: 'flow_mgr', - type: 'group', - fields: [ - { - name: 'rows_busy', - type: 'long', - }, - { - name: 'flows_timeout', - type: 'long', - }, - { - name: 'flows_notimeout', - type: 'long', - }, - { - name: 'rows_skipped', - type: 'long', - }, - { - name: 'closed_pruned', - type: 'long', - }, - { - name: 'new_pruned', - type: 'long', - }, - { - name: 'flows_removed', - type: 'long', - }, - { - name: 'bypassed_pruned', - type: 'long', - }, - { - name: 'est_pruned', - type: 'long', - }, - { - name: 'flows_timeout_inuse', - type: 'long', - }, - { - name: 'flows_checked', - type: 'long', - }, - { - name: 'rows_maxlen', - type: 'long', - }, - { - name: 'rows_checked', - type: 'long', - }, - { - name: 'rows_empty', - type: 'long', - }, - ], - }, - { - name: 'app_layer', - type: 'group', - fields: [ - { - name: 'flow', - type: 'group', - fields: [ - { - name: 'tls', - type: 'long', - }, - { - name: 'ftp', - type: 'long', - }, - { - name: 'http', - type: 'long', - }, - { - name: 'failed_udp', - type: 'long', - }, - { - name: 'dns_udp', - type: 'long', - }, - { - name: 'dns_tcp', - type: 'long', - }, - { - name: 'smtp', - type: 'long', - }, - { - name: 'failed_tcp', - type: 'long', - }, - { - name: 'msn', - type: 'long', - }, - { - name: 'ssh', - type: 'long', - }, - { - name: 'imap', - type: 'long', - }, - { - name: 'dcerpc_udp', - type: 'long', - }, - { - name: 'dcerpc_tcp', - type: 'long', - }, - { - name: 'smb', - type: 'long', - }, - ], - }, - { - name: 'tx', - type: 'group', - fields: [ - { - name: 'tls', - type: 'long', - }, - { - name: 'ftp', - type: 'long', - }, - { - name: 'http', - type: 'long', - }, - { - name: 'dns_udp', - type: 'long', - }, - { - name: 'dns_tcp', - type: 'long', - }, - { - name: 'smtp', - type: 'long', - }, - { - name: 'ssh', - type: 'long', - }, - { - name: 'dcerpc_udp', - type: 'long', - }, - { - name: 'dcerpc_tcp', - type: 'long', - }, - { - name: 'smb', - type: 'long', - }, - ], - }, - ], - }, - ], - }, - { - name: 'tls', - type: 'group', - fields: [ - { - name: 'notbefore', - type: 'date', - }, - { - name: 'issuerdn', - type: 'keyword', - }, - { - name: 'sni', - type: 'keyword', - }, - { - name: 'version', - type: 'keyword', - }, - { - name: 'session_resumed', - type: 'boolean', - }, - { - name: 'fingerprint', - type: 'keyword', - }, - { - name: 'serial', - type: 'keyword', - }, - { - name: 'notafter', - type: 'date', - }, - { - name: 'subject', - type: 'keyword', - }, - ], - }, - { - name: 'app_proto_ts', - type: 'keyword', - }, - { - name: 'flow', - type: 'group', - fields: [ - { - name: 'bytes_toclient', - type: 'alias', - path: 'destination.bytes', - }, - { - name: 'start', - type: 'alias', - path: 'event.start', - }, - { - name: 'pkts_toclient', - type: 'alias', - path: 'destination.packets', - }, - { - name: 'age', - type: 'long', - }, - { - name: 'state', - type: 'keyword', - }, - { - name: 'bytes_toserver', - type: 'alias', - path: 'source.bytes', - }, - { - name: 'reason', - type: 'keyword', - }, - { - name: 'pkts_toserver', - type: 'alias', - path: 'source.packets', - }, - { - name: 'end', - type: 'date', - }, - { - name: 'alerted', - type: 'boolean', - }, - ], - }, - { - name: 'app_proto', - type: 'alias', - path: 'network.protocol', - }, - { - name: 'tx_id', - type: 'long', - }, - { - name: 'app_proto_tc', - type: 'keyword', - }, - { - name: 'smtp', - type: 'group', - fields: [ - { - name: 'rcpt_to', - type: 'keyword', - }, - { - name: 'mail_from', - type: 'keyword', - }, - { - name: 'helo', - type: 'keyword', - }, - ], - }, - { - name: 'app_proto_expected', - type: 'keyword', - }, - { - name: 'flags', - type: 'group', - fields: [], - }, - ], - }, - ], - }, - ], - }, - { - key: 'zeek', - title: 'Zeek', - description: 'Module for handling logs produced by Zeek/Bro\n', - fields: [ - { - name: 'zeek', - type: 'group', - description: 'Fields from Zeek/Bro logs after normalization\n', - fields: [ - { - name: 'session_id', - type: 'keyword', - description: 'A unique identifier of the session\n', - }, - { - name: 'capture_loss', - type: 'group', - description: 'Fields exported by the Zeek capture_loss log\n', - fields: [ - { - name: 'ts_delta', - type: 'integer', - description: 'The time delay between this measurement and the last.\n', - }, - { - name: 'peer', - type: 'keyword', - description: - 'In the event that there are multiple Bro instances logging to the same host, this distinguishes each peer with its individual name.\n', - }, - { - name: 'gaps', - type: 'integer', - description: 'Number of missed ACKs from the previous measurement interval.\n', - }, - { - name: 'acks', - type: 'integer', - description: 'Total number of ACKs seen in the previous measurement interval.\n', - }, - { - name: 'percent_lost', - type: 'double', - description: "Percentage of ACKs seen where the data being ACKed wasn't seen.\n", - }, - ], - }, - { - name: 'connection', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek Connection log\n', - fields: [ - { - name: 'local_orig', - type: 'boolean', - description: 'Indicates whether the session is originated locally.\n', - }, - { - name: 'local_resp', - type: 'boolean', - description: 'Indicates whether the session is responded locally.\n', - }, - { - name: 'missed_bytes', - type: 'long', - description: 'Missed bytes for the session.\n', - }, - { - name: 'state', - type: 'keyword', - description: 'Code indicating the state of the session.\n', - }, - { - name: 'state_message', - type: 'keyword', - description: 'The state of the session.\n', - }, - { - name: 'icmp', - type: 'group', - fields: [ - { - name: 'type', - type: 'integer', - description: 'ICMP message type.\n', - }, - { - name: 'code', - type: 'integer', - description: 'ICMP message code.\n', - }, - ], - }, - { - name: 'history', - type: 'keyword', - description: 'Flags indicating the history of the session.\n', - }, - { - name: 'vlan', - type: 'integer', - description: 'VLAN identifier.\n', - }, - { - name: 'inner_vlan', - type: 'integer', - description: 'VLAN identifier.\n', - }, - ], - }, - { - name: 'dce_rpc', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek DCE_RPC log\n', - fields: [ - { - name: 'rtt', - type: 'integer', - description: - "Round trip time from the request to the response. If either the request or response wasn't seen, this will be null.\n", - }, - { - name: 'named_pipe', - type: 'keyword', - description: 'Remote pipe name.\n', - }, - { - name: 'endpoint', - type: 'keyword', - description: 'Endpoint name looked up from the uuid.\n', - }, - { - name: 'operation', - type: 'keyword', - description: 'Operation seen in the call.\n', - }, - ], - }, - { - name: 'dhcp', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek DHCP log\n', - fields: [ - { - name: 'domain', - type: 'keyword', - description: 'Domain given by the server in option 15.\n', - }, - { - name: 'duration', - type: 'double', - description: - 'Duration of the DHCP session representing the time from the first\nmessage to the last, in seconds.\n', - }, - { - name: 'hostname', - type: 'keyword', - description: 'Name given by client in Hostname option 12.\n', - }, - { - name: 'client_fqdn', - type: 'keyword', - description: 'FQDN given by client in Client FQDN option 81.\n', - }, - { - name: 'lease_time', - type: 'integer', - description: 'IP address lease interval in seconds.\n', - }, - { - name: 'address', - type: 'group', - description: 'Addresses seen in this DHCP exchange.\n', - fields: [ - { - name: 'assigned', - type: 'ip', - description: 'IP address assigned by the server.\n', - }, - { - name: 'client', - type: 'ip', - description: - 'IP address of the client. If a transaction is only a client sending\nINFORM messages then there is no lease information exchanged so this\nis helpful to know who sent the messages. Getting an address in this\nfield does require that the client sources at least one DHCP message\nusing a non-broadcast address.\n', - }, - { - name: 'mac', - type: 'keyword', - description: "Client's hardware address.\n", - }, - { - name: 'requested', - type: 'ip', - description: 'IP address requested by the client.\n', - }, - { - name: 'server', - type: 'ip', - description: 'IP address of the DHCP server.\n', - }, - ], - }, - { - name: 'msg', - type: 'group', - fields: [ - { - name: 'types', - type: 'keyword', - description: 'List of DHCP message types seen in this exchange.\n', - }, - { - name: 'origin', - type: 'ip', - description: - '(present if policy/protocols/dhcp/msg-orig.bro is loaded)\nThe address that originated each message from the msg.types field.\n', - }, - { - name: 'client', - type: 'keyword', - description: - 'Message typically accompanied with a DHCP_DECLINE so the client can\ntell the server why it rejected an address.\n', - }, - { - name: 'server', - type: 'keyword', - description: - 'Message typically accompanied with a DHCP_NAK to let the client know\nwhy it rejected the request.\n', - }, - ], - }, - { - name: 'software', - type: 'group', - fields: [ - { - name: 'client', - type: 'keyword', - description: - '(present if policy/protocols/dhcp/software.bro is loaded)\nSoftware reported by the client in the vendor_class option.\n', - }, - { - name: 'server', - type: 'keyword', - description: - '(present if policy/protocols/dhcp/software.bro is loaded)\nSoftware reported by the client in the vendor_class option.\n', - }, - ], - }, - { - name: 'id', - type: 'group', - fields: [ - { - name: 'circuit', - type: 'keyword', - description: - '(present if policy/protocols/dhcp/sub-opts.bro is loaded)\nAdded by DHCP relay agents which terminate switched or permanent\ncircuits. It encodes an agent-local identifier of the circuit from\nwhich a DHCP client-to-server packet was received. Typically it\nshould represent a router or switch interface number.\n', - }, - { - name: 'remote_agent', - type: 'keyword', - description: - '(present if policy/protocols/dhcp/sub-opts.bro is loaded)\nA globally unique identifier added by relay agents to identify the\nremote host end of the circuit.\n', - }, - { - name: 'subscriber', - type: 'keyword', - description: - "(present if policy/protocols/dhcp/sub-opts.bro is loaded)\nThe subscriber ID is a value independent of the physical network\nconfiguration so that a customer's DHCP configuration can be given\nto them correctly no matter where they are physically connected.\n", - }, - ], - }, - ], - }, - { - name: 'dnp3', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek SSH log\n', - fields: [ - { - name: 'function', - type: 'group', - fields: [ - { - name: 'request', - type: 'keyword', - description: 'The name of the function message in the request.\n', - }, - { - name: 'reply', - type: 'keyword', - description: 'The name of the function message in the reply.\n', - }, - ], - }, - { - name: 'id', - type: 'integer', - description: "The response's internal indication number.\n", - }, - ], - }, - { - name: 'dns', - type: 'group', - description: 'Fields exported by the Zeek DNS log\n', - fields: [ - { - name: 'trans_id', - type: 'keyword', - description: 'DNS transaction identifier.\n', - }, - { - name: 'rtt', - type: 'double', - description: 'Round trip time for the query and response.\n', - }, - { - name: 'query', - type: 'keyword', - description: 'The domain name that is the subject of the DNS query.\n', - }, - { - name: 'qclass', - type: 'long', - description: 'The QCLASS value specifying the class of the query.\n', - }, - { - name: 'qclass_name', - type: 'keyword', - description: 'A descriptive name for the class of the query.\n', - }, - { - name: 'qtype', - type: 'long', - description: 'A QTYPE value specifying the type of the query.\n', - }, - { - name: 'qtype_name', - type: 'keyword', - description: 'A descriptive name for the type of the query.\n', - }, - { - name: 'rcode', - type: 'long', - description: 'The response code value in DNS response messages.\n', - }, - { - name: 'rcode_name', - type: 'keyword', - description: 'A descriptive name for the response code value.\n', - }, - { - name: 'AA', - type: 'boolean', - description: - 'The Authoritative Answer bit for response messages specifies that the responding\nname server is an authority for the domain name in the question section.\n', - }, - { - name: 'TC', - type: 'boolean', - description: 'The Truncation bit specifies that the message was truncated.\n', - }, - { - name: 'RD', - type: 'boolean', - description: - 'The Recursion Desired bit in a request message indicates that the client\nwants recursive service for this query.\n', - }, - { - name: 'RA', - type: 'boolean', - description: - 'The Recursion Available bit in a response message indicates that the name\nserver supports recursive queries.\n', - }, - { - name: 'answers', - type: 'keyword', - description: 'The set of resource descriptions in the query answer.\n', - }, - { - name: 'TTLs', - type: 'double', - description: - 'The caching intervals of the associated RRs described by the answers field.\n', - }, - { - name: 'rejected', - type: 'boolean', - description: 'Indicates whether the DNS query was rejected by the server.\n', - }, - { - name: 'total_answers', - type: 'integer', - description: 'The total number of resource records in the reply.\n', - }, - { - name: 'total_replies', - type: 'integer', - description: 'The total number of resource records in the reply message.\n', - }, - { - name: 'saw_query', - type: 'boolean', - description: 'Whether the full DNS query has been seen.\n', - }, - { - name: 'saw_reply', - type: 'boolean', - description: 'Whether the full DNS reply has been seen.\n', - }, - ], - }, - { - name: 'dpd', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek DPD log\n', - fields: [ - { - name: 'analyzer', - type: 'keyword', - description: 'The analyzer that generated the violation.\n', - }, - { - name: 'failure_reason', - type: 'keyword', - description: 'The textual reason for the analysis failure.\n', - }, - { - name: 'packet_segment', - type: 'keyword', - description: - '(present if policy/frameworks/dpd/packet-segment-logging.bro is loaded)\nA chunk of the payload that most likely resulted in the protocol violation.\n', - }, - ], - }, - { - name: 'files', - type: 'group', - description: 'Fields exported by the Zeek Files log.\n', - fields: [ - { - name: 'fuid', - type: 'keyword', - description: 'A file unique identifier.\n', - }, - { - name: 'tx_host', - type: 'ip', - description: 'The host that transferred the file.\n', - }, - { - name: 'rx_host', - type: 'ip', - description: 'The host that received the file.\n', - }, - { - name: 'session_ids', - type: 'keyword', - description: 'The sessions that have this file.\n', - }, - { - name: 'source', - type: 'keyword', - description: - 'An identification of the source of the file data. E.g. it may be a network protocol\nover which it was transferred, or a local file path which was read, or some other\ninput source.\n', - }, - { - name: 'depth', - type: 'long', - description: - 'A value to represent the depth of this file in relation to its source. In SMTP, it\nis the depth of the MIME attachment on the message. In HTTP, it is the depth of the\nrequest within the TCP connection.\n', - }, - { - name: 'analyzers', - type: 'keyword', - description: 'A set of analysis types done during the file analysis.\n', - }, - { - name: 'mime_type', - type: 'keyword', - description: 'Mime type of the file.\n', - }, - { - name: 'filename', - type: 'keyword', - description: 'Name of the file if available.\n', - }, - { - name: 'local_orig', - type: 'boolean', - description: - 'If the source of this file is a network connection, this field indicates if the data\noriginated from the local network or not.\n', - }, - { - name: 'is_orig', - type: 'boolean', - description: - 'If the source of this file is a network connection, this field indicates if the file is\nbeing sent by the originator of the connection or the responder.\n', - }, - { - name: 'duration', - type: 'double', - description: - 'The duration the file was analyzed for. Not the duration of the session.\n', - }, - { - name: 'seen_bytes', - type: 'long', - description: 'Number of bytes provided to the file analysis engine for the file.\n', - }, - { - name: 'total_bytes', - type: 'long', - description: 'Total number of bytes that are supposed to comprise the full file.\n', - }, - { - name: 'missing_bytes', - type: 'long', - description: - 'The number of bytes in the file stream that were completely missed during the process\nof analysis.\n', - }, - { - name: 'overflow_bytes', - type: 'long', - description: - "The number of bytes in the file stream that were not delivered to stream file analyzers.\nThis could be overlapping bytes or bytes that couldn't be reassembled.\n", - }, - { - name: 'timedout', - type: 'boolean', - description: 'Whether the file analysis timed out at least once for the file.\n', - }, - { - name: 'parent_fuid', - type: 'keyword', - description: - 'Identifier associated with a container file from which this one was extracted as part of\nthe file analysis.\n', - }, - { - name: 'md5', - type: 'keyword', - description: 'An MD5 digest of the file contents.\n', - }, - { - name: 'sha1', - type: 'keyword', - description: 'A SHA1 digest of the file contents.\n', - }, - { - name: 'sha256', - type: 'keyword', - description: 'A SHA256 digest of the file contents.\n', - }, - { - name: 'extracted', - type: 'keyword', - description: 'Local filename of extracted file.\n', - }, - { - name: 'extracted_cutoff', - type: 'boolean', - description: - 'Indicate whether the file being extracted was cut off hence not extracted completely.\n', - }, - { - name: 'extracted_size', - type: 'long', - description: 'The number of bytes extracted to disk.\n', - }, - { - name: 'entropy', - type: 'double', - description: 'The information density of the contents of the file.\n', - }, - ], - }, - { - name: 'ftp', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek FTP log\n', - fields: [ - { - name: 'user', - type: 'keyword', - description: 'User name for the current FTP session.\n', - }, - { - name: 'password', - type: 'keyword', - description: 'Password for the current FTP session if captured.\n', - }, - { - name: 'command', - type: 'keyword', - description: 'Command given by the client.\n', - }, - { - name: 'arg', - type: 'keyword', - description: 'Argument for the command if one is given.\n', - }, - { - name: 'file', - type: 'group', - fields: [ - { - name: 'size', - type: 'long', - description: 'Size of the file if the command indicates a file transfer.\n', - }, - { - name: 'mime_type', - type: 'keyword', - description: 'Sniffed mime type of file.\n', - }, - { - name: 'fuid', - type: 'keyword', - description: - '(present if base/protocols/ftp/files.bro is loaded)\nFile unique ID.\n', - }, - ], - }, - { - name: 'reply', - type: 'group', - fields: [ - { - name: 'code', - type: 'integer', - description: 'Reply code from the server in response to the command.\n', - }, - { - name: 'msg', - type: 'keyword', - description: 'Reply message from the server in response to the command.\n', - }, - ], - }, - { - name: 'data_channel', - type: 'group', - description: 'Expected FTP data channel.\n', - fields: [ - { - name: 'passive', - type: 'boolean', - description: 'Whether PASV mode is toggled for control channel.\n', - }, - { - name: 'originating_host', - type: 'ip', - description: 'The host that will be initiating the data connection.\n', - }, - { - name: 'response_host', - type: 'ip', - description: 'The host that will be accepting the data connection.\n', - }, - { - name: 'response_port', - type: 'integer', - description: - 'The port at which the acceptor is listening for the data connection.\n', - }, - ], - }, - { - name: 'cwd', - type: 'keyword', - description: - "Current working directory that this session is in. By making the default value '.', we can indicate that unless something more concrete is discovered that the existing but unknown directory is ok to use.\n", - }, - { - name: 'cmdarg', - type: 'group', - description: 'Command that is currently waiting for a response.\n', - fields: [ - { - name: 'cmd', - type: 'keyword', - description: 'Command.\n', - }, - { - name: 'arg', - type: 'keyword', - description: 'Argument for the command if one was given.\n', - }, - { - name: 'seq', - type: 'integer', - description: 'Counter to track how many commands have been executed.\n', - }, - ], - }, - { - name: 'pending_commands', - type: 'integer', - description: - 'Queue for commands that have been sent but not yet responded to are tracked here.\n', - }, - { - name: 'passive', - type: 'boolean', - description: 'Indicates if the session is in active or passive mode.\n', - }, - { - name: 'capture_password', - type: 'boolean', - description: 'Determines if the password will be captured for this request.\n', - }, - { - name: 'last_auth_requested', - type: 'keyword', - description: - 'present if base/protocols/ftp/gridftp.bro is loaded.\nLast authentication/security mechanism that was used.\n', - }, - ], - }, - { - name: 'http', - type: 'group', - description: 'Fields exported by the Zeek HTTP log\n', - fields: [ - { - name: 'trans_depth', - type: 'integer', - description: - 'Represents the pipelined depth into the connection of this request/response transaction.\n', - }, - { - name: 'status_msg', - type: 'keyword', - description: 'Status message returned by the server.\n', - }, - { - name: 'info_code', - type: 'integer', - description: 'Last seen 1xx informational reply code returned by the server.\n', - }, - { - name: 'info_msg', - type: 'keyword', - description: 'Last seen 1xx informational reply message returned by the server.\n', - }, - { - name: 'tags', - type: 'keyword', - description: - 'A set of indicators of various attributes discovered and related to a particular\nrequest/response pair.\n', - }, - { - name: 'password', - type: 'keyword', - description: 'Password if basic-auth is performed for the request.\n', - }, - { - name: 'captured_password', - type: 'boolean', - description: 'Determines if the password will be captured for this request.\n', - }, - { - name: 'proxied', - type: 'keyword', - description: - 'All of the headers that may indicate if the HTTP request was proxied.\n', - }, - { - name: 'range_request', - type: 'boolean', - description: - 'Indicates if this request can assume 206 partial content in response.\n', - }, - { - name: 'client_header_names', - type: 'keyword', - description: - 'The vector of HTTP header names sent by the client. No header values\nare included here, just the header names.\n', - }, - { - name: 'server_header_names', - type: 'keyword', - description: - 'The vector of HTTP header names sent by the server. No header values\nare included here, just the header names.\n', - }, - { - name: 'orig_fuids', - type: 'keyword', - description: 'An ordered vector of file unique IDs from the originator.\n', - }, - { - name: 'orig_mime_types', - type: 'keyword', - description: 'An ordered vector of mime types from the originator.\n', - }, - { - name: 'orig_filenames', - type: 'keyword', - description: 'An ordered vector of filenames from the originator.\n', - }, - { - name: 'resp_fuids', - type: 'keyword', - description: 'An ordered vector of file unique IDs from the responder.\n', - }, - { - name: 'resp_mime_types', - type: 'keyword', - description: 'An ordered vector of mime types from the responder.\n', - }, - { - name: 'resp_filenames', - type: 'keyword', - description: 'An ordered vector of filenames from the responder.\n', - }, - { - name: 'orig_mime_depth', - type: 'integer', - description: 'Current number of MIME entities in the HTTP request message body.\n', - }, - { - name: 'resp_mime_depth', - type: 'integer', - description: 'Current number of MIME entities in the HTTP response message body.\n', - }, - ], - }, - { - name: 'intel', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek Intel log.\n', - fields: [ - { - name: 'seen', - type: 'group', - fields: [ - { - name: 'indicator', - type: 'keyword', - description: 'The intelligence indicator.\n', - }, - { - name: 'indicator_type', - type: 'keyword', - description: 'The type of data the indicator represents.\n', - }, - { - name: 'host', - type: 'keyword', - description: - 'If the indicator type was Intel::ADDR, then this field will be present.\n', - }, - { - name: 'conn', - type: 'keyword', - description: - 'If the data was discovered within a connection, the connection record should go here to give context to the data.\n', - }, - { - name: 'where', - type: 'keyword', - description: 'Where the data was discovered.\n', - }, - { - name: 'node', - type: 'keyword', - description: 'The name of the node where the match was discovered.\n', - }, - { - name: 'uid', - type: 'keyword', - description: - 'If the data was discovered within a connection, the connection uid should go here to give context to the data. If the conn field is provided, this will be automatically filled out.\n', - }, - { - name: 'f', - type: 'object', - description: - 'If the data was discovered within a file, the file record should go here to provide context to the data.\n', - }, - { - name: 'fuid', - type: 'keyword', - description: - 'If the data was discovered within a file, the file uid should go here to provide context to the data. If the file record f is provided, this will be automatically filled out.\n', - }, - ], - }, - { - name: 'matched', - type: 'keyword', - description: - 'Event to represent a match in the intelligence data from data that was seen.\n', - }, - { - name: 'sources', - type: 'keyword', - description: 'Sources which supplied data for this match.\n', - }, - { - name: 'fuid', - type: 'keyword', - description: - 'If a file was associated with this intelligence hit, this is the uid for the file.\n', - }, - { - name: 'file_mime_type', - type: 'keyword', - description: - 'A mime type if the intelligence hit is related to a file. If the $f field is provided this will be automatically filled out.\n', - }, - { - name: 'file_desc', - type: 'keyword', - description: - 'Frequently files can be described to give a bit more context. If the $f field is provided this field will be automatically filled out.\n', - }, - ], - }, - { - name: 'irc', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek IRC log\n', - fields: [ - { - name: 'nick', - type: 'keyword', - description: 'Nickname given for the connection.\n', - }, - { - name: 'user', - type: 'keyword', - description: 'Username given for the connection.\n', - }, - { - name: 'command', - type: 'keyword', - description: 'Command given by the client.\n', - }, - { - name: 'value', - type: 'keyword', - description: 'Value for the command given by the client.\n', - }, - { - name: 'addl', - type: 'keyword', - description: 'Any additional data for the command.\n', - }, - { - name: 'dcc', - type: 'group', - fields: [ - { - name: 'file', - type: 'group', - fields: [ - { - name: 'name', - type: 'keyword', - description: - 'Present if base/protocols/irc/dcc-send.bro is loaded.\nDCC filename requested.\n', - }, - { - name: 'size', - type: 'long', - description: - 'Present if base/protocols/irc/dcc-send.bro is loaded.\nSize of the DCC transfer as indicated by the sender.\n', - }, - ], - }, - { - name: 'mime_type', - type: 'keyword', - description: - 'present if base/protocols/irc/dcc-send.bro is loaded.\nSniffed mime type of the file.\n', - }, - ], - }, - { - name: 'fuid', - type: 'keyword', - description: - 'present if base/protocols/irc/files.bro is loaded.\nFile unique ID.\n', - }, - ], - }, - { - name: 'kerberos', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek Kerberos log\n', - fields: [ - { - name: 'request_type', - type: 'keyword', - description: - 'Request type - Authentication Service (AS) or Ticket Granting Service (TGS).\n', - }, - { - name: 'client', - type: 'keyword', - description: 'Client name.\n', - }, - { - name: 'service', - type: 'keyword', - description: 'Service name.\n', - }, - { - name: 'success', - type: 'boolean', - description: 'Request result.\n', - }, - { - name: 'error', - type: 'group', - fields: [ - { - name: 'code', - type: 'integer', - description: 'Error code.\n', - }, - { - name: 'msg', - type: 'keyword', - description: 'Error message.\n', - }, - ], - }, - { - name: 'valid', - type: 'group', - fields: [ - { - name: 'from', - type: 'date', - description: 'Ticket valid from.\n', - }, - { - name: 'until', - type: 'date', - description: 'Ticket valid until.\n', - }, - { - name: 'days', - type: 'integer', - description: 'Number of days the ticket is valid for.\n', - }, - ], - }, - { - name: 'cipher', - type: 'keyword', - description: 'Ticket encryption type.\n', - }, - { - name: 'forwardable', - type: 'boolean', - description: 'Forwardable ticket requested.\n', - }, - { - name: 'renewable', - type: 'boolean', - description: 'Renewable ticket requested.\n', - }, - { - name: 'ticket', - type: 'group', - fields: [ - { - name: 'auth', - type: 'keyword', - description: 'Hash of ticket used to authorize request/transaction.\n', - }, - { - name: 'new', - type: 'keyword', - description: 'Hash of ticket returned by the KDC.\n', - }, - ], - }, - { - name: 'cert', - type: 'group', - fields: [ - { - name: 'client', - type: 'group', - fields: [ - { - name: 'value', - type: 'keyword', - description: 'Client certificate.\n', - }, - { - name: 'fuid', - type: 'keyword', - description: 'File unique ID of client cert.\n', - }, - { - name: 'subject', - type: 'keyword', - description: 'Subject of client certificate.\n', - }, - ], - }, - { - name: 'server', - type: 'group', - fields: [ - { - name: 'value', - type: 'keyword', - description: 'Server certificate.\n', - }, - { - name: 'fuid', - type: 'keyword', - description: 'File unique ID of server certificate.\n', - }, - { - name: 'subject', - type: 'keyword', - description: 'Subject of server certificate.\n', - }, - ], - }, - ], - }, - ], - }, - { - name: 'modbus', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek modbus log.\n', - fields: [ - { - name: 'function', - type: 'keyword', - description: 'The name of the function message that was sent.\n', - }, - { - name: 'exception', - type: 'keyword', - description: 'The exception if the response was a failure.\n', - }, - { - name: 'track_address', - type: 'integer', - description: - 'Present if policy/protocols/modbus/track-memmap.bro is loaded.\nModbus track address.\n', - }, - ], - }, - { - name: 'mysql', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek MySQL log.\n', - fields: [ - { - name: 'cmd', - type: 'keyword', - description: 'The command that was issued.\n', - }, - { - name: 'arg', - type: 'keyword', - description: 'The argument issued to the command.\n', - }, - { - name: 'success', - type: 'boolean', - description: 'Whether the command succeeded.\n', - }, - { - name: 'rows', - type: 'integer', - description: 'The number of affected rows, if any.\n', - }, - { - name: 'response', - type: 'keyword', - description: 'Server message, if any.\n', - }, - ], - }, - { - name: 'notice', - type: 'group', - description: 'Fields exported by the Zeek Notice log.\n', - fields: [ - { - name: 'connection_id', - type: 'keyword', - description: 'Identifier of the related connection session.\n', - }, - { - name: 'icmp_id', - type: 'keyword', - description: 'Identifier of the related ICMP session.\n', - }, - { - name: 'file.id', - type: 'keyword', - description: - 'An identifier associated with a single file that is related to this notice.\n', - }, - { - name: 'file.parent_id', - type: 'keyword', - description: - 'Identifier associated with a container file from which this one was extracted.\n', - }, - { - name: 'file.source', - type: 'keyword', - description: - 'An identification of the source of the file data. E.g. it may be a network protocol\nover which it was transferred, or a local file path which was read, or some other\ninput source.\n', - }, - { - name: 'file.mime_type', - type: 'keyword', - description: 'A mime type if the notice is related to a file.\n', - }, - { - name: 'file.is_orig', - type: 'boolean', - description: - 'If the source of this file is a network connection, this field indicates if the file is\nbeing sent by the originator of the connection or the responder.\n', - }, - { - name: 'file.seen_bytes', - type: 'long', - description: 'Number of bytes provided to the file analysis engine for the file.\n', - }, - { - name: 'ffile.total_bytes', - type: 'long', - description: 'Total number of bytes that are supposed to comprise the full file.\n', - }, - { - name: 'file.missing_bytes', - type: 'long', - description: - 'The number of bytes in the file stream that were completely missed during the process\nof analysis.\n', - }, - { - name: 'file.overflow_bytes', - type: 'long', - description: - "The number of bytes in the file stream that were not delivered to stream file analyzers.\nThis could be overlapping bytes or bytes that couldn't be reassembled.\n", - }, - { - name: 'fuid', - type: 'keyword', - description: 'A file unique ID if this notice is related to a file.\n', - }, - { - name: 'note', - type: 'keyword', - description: 'The type of the notice.\n', - }, - { - name: 'msg', - type: 'keyword', - description: 'The human readable message for the notice.\n', - }, - { - name: 'sub', - type: 'keyword', - description: 'The human readable sub-message.\n', - }, - { - name: 'n', - type: 'long', - description: 'Associated count, or a status code.\n', - }, - { - name: 'peer_name', - type: 'keyword', - description: 'Name of remote peer that raised this notice.\n', - }, - { - name: 'peer_descr', - type: 'text', - description: 'Textual description for the peer that raised this notice.\n', - }, - { - name: 'actions', - type: 'keyword', - description: 'The actions which have been applied to this notice.\n', - }, - { - name: 'email_body_sections', - type: 'text', - description: - 'By adding chunks of text into this element, other scripts can expand on notices\nthat are being emailed.\n', - }, - { - name: 'email_delay_tokens', - type: 'keyword', - description: - 'Adding a string token to this set will cause the built-in emailing functionality\nto delay sending the email either the token has been removed or the email\nhas been delayed for the specified time duration.\n', - }, - { - name: 'identifier', - type: 'keyword', - description: - 'This field is provided when a notice is generated for the purpose of deduplicating notices.\n', - }, - { - name: 'suppress_for', - type: 'double', - description: - 'This field indicates the length of time that this unique notice should be suppressed.\n', - }, - { - name: 'dropped', - type: 'boolean', - description: - 'Indicate if the source IP address was dropped and denied network access.\n', - }, - ], - }, - { - name: 'ntlm', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek NTLM log.\n', - fields: [ - { - name: 'domain', - type: 'keyword', - description: 'Domain name given by the client.\n', - }, - { - name: 'hostname', - type: 'keyword', - description: 'Hostname given by the client.\n', - }, - { - name: 'success', - type: 'boolean', - description: 'Indicate whether or not the authentication was successful.\n', - }, - { - name: 'username', - type: 'keyword', - description: 'Username given by the client.\n', - }, - { - name: 'server', - type: 'group', - fields: [ - { - name: 'name', - type: 'group', - fields: [ - { - name: 'dns', - type: 'keyword', - description: 'DNS name given by the server in a CHALLENGE.\n', - }, - { - name: 'netbios', - type: 'keyword', - description: 'NetBIOS name given by the server in a CHALLENGE.\n', - }, - { - name: 'tree', - type: 'keyword', - description: 'Tree name given by the server in a CHALLENGE.\n', - }, - ], - }, - ], - }, - ], - }, - { - name: 'ocsp', - type: 'group', - default_field: false, - description: - 'Fields exported by the Zeek OCSP log\nOnline Certificate Status Protocol (OCSP). Only created if policy script is loaded.\n', - fields: [ - { - name: 'file_id', - type: 'keyword', - description: 'File id of the OCSP reply.\n', - }, - { - name: 'hash', - type: 'group', - fields: [ - { - name: 'algorithm', - type: 'keyword', - description: - 'Hash algorithm used to generate issuerNameHash and issuerKeyHash.\n', - }, - { - name: 'issuer', - type: 'group', - fields: [ - { - name: 'name', - type: 'keyword', - description: "Hash of the issuer's distingueshed name.\n", - }, - { - name: 'key', - type: 'keyword', - description: "Hash of the issuer's public key.\n", - }, - ], - }, - ], - }, - { - name: 'serial_number', - type: 'keyword', - description: 'Serial number of the affected certificate.\n', - }, - { - name: 'status', - type: 'keyword', - description: 'Status of the affected certificate.\n', - }, - { - name: 'revoke', - type: 'group', - fields: [ - { - name: 'time', - type: 'date', - description: 'Time at which the certificate was revoked.\n', - }, - { - name: 'reason', - type: 'keyword', - description: 'Reason for which the certificate was revoked.\n', - }, - ], - }, - { - name: 'update', - type: 'group', - fields: [ - { - name: 'this', - type: 'date', - description: - 'The time at which the status being shows is known to have been correct.\n', - }, - { - name: 'next', - type: 'date', - description: - 'The latest time at which new information about the status of the certificate will be available.\n', - }, - ], - }, - ], - }, - { - name: 'pe', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek pe log.\n', - fields: [ - { - name: 'client', - type: 'keyword', - description: "The client's version string.\n", - }, - { - name: 'id', - type: 'keyword', - description: 'File id of this portable executable file.\n', - }, - { - name: 'machine', - type: 'keyword', - description: 'The target machine that the file was compiled for.\n', - }, - { - name: 'compile_time', - type: 'date', - description: 'The time that the file was created at.\n', - }, - { - name: 'os', - type: 'keyword', - description: 'The required operating system.\n', - }, - { - name: 'subsystem', - type: 'keyword', - description: 'The subsystem that is required to run this file.\n', - }, - { - name: 'is_exe', - type: 'boolean', - description: 'Is the file an executable, or just an object file?\n', - }, - { - name: 'is_64bit', - type: 'boolean', - description: 'Is the file a 64-bit executable?\n', - }, - { - name: 'uses_aslr', - type: 'boolean', - description: 'Does the file support Address Space Layout Randomization?\n', - }, - { - name: 'uses_dep', - type: 'boolean', - description: 'Does the file support Data Execution Prevention?\n', - }, - { - name: 'uses_code_integrity', - type: 'boolean', - description: 'Does the file enforce code integrity checks?\n', - }, - { - name: 'uses_seh', - type: 'boolean', - description: 'Does the file use structured exception handing?\n', - }, - { - name: 'has_import_table', - type: 'boolean', - description: 'Does the file have an import table?\n', - }, - { - name: 'has_export_table', - type: 'boolean', - description: 'Does the file have an export table?\n', - }, - { - name: 'has_cert_table', - type: 'boolean', - description: 'Does the file have an attribute certificate table?\n', - }, - { - name: 'has_debug_data', - type: 'boolean', - description: 'Does the file have a debug table?\n', - }, - { - name: 'section_names', - type: 'keyword', - description: 'The names of the sections, in order.\n', - }, - ], - }, - { - name: 'radius', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek Radius log.\n', - fields: [ - { - name: 'username', - type: 'keyword', - description: 'The username, if present.\n', - }, - { - name: 'mac', - type: 'keyword', - description: 'MAC address, if present.\n', - }, - { - name: 'framed_addr', - type: 'ip', - description: - 'The address given to the network access server, if present. This is only a hint from the RADIUS server and the network access server is not required to honor the address.\n', - }, - { - name: 'remote_ip', - type: 'ip', - description: - 'Remote IP address, if present. This is collected from the Tunnel-Client-Endpoint attribute.\n', - }, - { - name: 'connect_info', - type: 'keyword', - description: 'Connect info, if present.\n', - }, - { - name: 'reply_msg', - type: 'keyword', - description: - 'Reply message from the server challenge. This is frequently shown to the user authenticating.\n', - }, - { - name: 'result', - type: 'keyword', - description: 'Successful or failed authentication.\n', - }, - { - name: 'ttl', - type: 'integer', - description: - 'The duration between the first request and either the "Access-Accept" message or an error. If the field is empty, it means that either the request or response was not seen.\n', - }, - { - name: 'logged', - type: 'boolean', - description: 'Whether this has already been logged and can be ignored.\n', - }, - ], - }, - { - name: 'rdp', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek RDP log.\n', - fields: [ - { - name: 'cookie', - type: 'keyword', - description: - 'Cookie value used by the client machine. This is typically a username.\n', - }, - { - name: 'result', - type: 'keyword', - description: - "Status result for the connection. It's a mix between RDP negotation failure messages and GCC server create response messages.\n", - }, - { - name: 'security_protocol', - type: 'keyword', - description: 'Security protocol chosen by the server.\n', - }, - { - name: 'keyboard_layout', - type: 'keyword', - description: 'Keyboard layout (language) of the client machine.\n', - }, - { - name: 'client', - type: 'group', - fields: [ - { - name: 'build', - type: 'keyword', - description: 'RDP client version used by the client machine.\n', - }, - { - name: 'client_name', - type: 'keyword', - description: 'Name of the client machine.\n', - }, - { - name: 'product_id', - type: 'keyword', - description: 'Product ID of the client machine.\n', - }, - ], - }, - { - name: 'desktop', - type: 'group', - fields: [ - { - name: 'width', - type: 'integer', - description: 'Desktop width of the client machine.\n', - }, - { - name: 'height', - type: 'integer', - description: 'Desktop height of the client machine.\n', - }, - { - name: 'color_depth', - type: 'keyword', - description: - 'The color depth requested by the client in the high_color_depth field.\n', - }, - ], - }, - { - name: 'cert', - type: 'group', - fields: [ - { - name: 'type', - type: 'keyword', - description: - 'If the connection is being encrypted with native RDP encryption, this is the type of cert being used.\n', - }, - { - name: 'count', - type: 'integer', - description: - 'The number of certs seen. X.509 can transfer an entire certificate chain.\n', - }, - { - name: 'permanent', - type: 'boolean', - description: - 'Indicates if the provided certificate or certificate chain is permanent or temporary.\n', - }, - ], - }, - { - name: 'encryption', - type: 'group', - fields: [ - { - name: 'level', - type: 'keyword', - description: 'Encryption level of the connection.\n', - }, - { - name: 'method', - type: 'keyword', - description: 'Encryption method of the connection.\n', - }, - ], - }, - { - name: 'done', - type: 'boolean', - description: 'Track status of logging RDP connections.\n', - }, - { - name: 'ssl', - type: 'boolean', - description: - '(present if policy/protocols/rdp/indicate_ssl.bro is loaded)\nFlag the connection if it was seen over SSL.\n', - }, - ], - }, - { - name: 'rfb', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek RFB log.\n', - fields: [ - { - name: 'version', - type: 'group', - fields: [ - { - name: 'client', - type: 'group', - fields: [ - { - name: 'major', - type: 'keyword', - description: 'Major version of the client.\n', - }, - { - name: 'minor', - type: 'keyword', - description: 'Minor version of the client.\n', - }, - ], - }, - { - name: 'server', - type: 'group', - fields: [ - { - name: 'major', - type: 'keyword', - description: 'Major version of the server.\n', - }, - { - name: 'minor', - type: 'keyword', - description: 'Minor version of the server.\n', - }, - ], - }, - ], - }, - { - name: 'auth', - type: 'group', - fields: [ - { - name: 'success', - type: 'boolean', - description: 'Whether or not authentication was successful.\n', - }, - { - name: 'method', - type: 'keyword', - description: 'Identifier of authentication method used.\n', - }, - ], - }, - { - name: 'share_flag', - type: 'boolean', - description: 'Whether the client has an exclusive or a shared session.\n', - }, - { - name: 'desktop_name', - type: 'keyword', - description: 'Name of the screen that is being shared.\n', - }, - { - name: 'width', - type: 'integer', - description: 'Width of the screen that is being shared.\n', - }, - { - name: 'height', - type: 'integer', - description: 'Height of the screen that is being shared.\n', - }, - ], - }, - { - name: 'sip', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek SIP log.\n', - fields: [ - { - name: 'transaction_depth', - type: 'integer', - description: - 'Represents the pipelined depth into the connection of this request/response transaction.\n', - }, - { - name: 'sequence', - type: 'group', - fields: [ - { - name: 'method', - type: 'keyword', - description: 'Verb used in the SIP request (INVITE, REGISTER etc.).\n', - }, - { - name: 'number', - type: 'keyword', - description: 'Contents of the CSeq: header from the client.\n', - }, - ], - }, - { - name: 'uri', - type: 'keyword', - description: 'URI used in the request.\n', - }, - { - name: 'date', - type: 'keyword', - description: 'Contents of the Date: header from the client.\n', - }, - { - name: 'request', - type: 'group', - fields: [ - { - name: 'from', - type: 'keyword', - description: - "Contents of the request From: header Note: The tag= value that's usually appended to the sender is stripped off and not logged.\n", - }, - { - name: 'to', - type: 'keyword', - description: 'Contents of the To: header.\n', - }, - { - name: 'path', - type: 'keyword', - description: - 'The client message transmission path, as extracted from the headers.\n', - }, - { - name: 'body_length', - type: 'long', - description: 'Contents of the Content-Length: header from the client.\n', - }, - ], - }, - { - name: 'response', - type: 'group', - fields: [ - { - name: 'from', - type: 'keyword', - description: - "Contents of the response From: header Note: The tag= value that's usually appended to the sender is stripped off and not logged.\n", - }, - { - name: 'to', - type: 'keyword', - description: 'Contents of the response To: header.\n', - }, - { - name: 'path', - type: 'keyword', - description: - 'The server message transmission path, as extracted from the headers.\n', - }, - { - name: 'body_length', - type: 'long', - description: 'Contents of the Content-Length: header from the server.\n', - }, - ], - }, - { - name: 'reply_to', - type: 'keyword', - description: 'Contents of the Reply-To: header.\n', - }, - { - name: 'call_id', - type: 'keyword', - description: 'Contents of the Call-ID: header from the client.\n', - }, - { - name: 'subject', - type: 'keyword', - description: 'Contents of the Subject: header from the client.\n', - }, - { - name: 'user_agent', - type: 'keyword', - description: 'Contents of the User-Agent: header from the client.\n', - }, - { - name: 'status', - type: 'group', - fields: [ - { - name: 'code', - type: 'integer', - description: 'Status code returned by the server.\n', - }, - { - name: 'msg', - type: 'keyword', - description: 'Status message returned by the server.\n', - }, - ], - }, - { - name: 'warning', - type: 'keyword', - description: 'Contents of the Warning: header.\n', - }, - { - name: 'content_type', - type: 'keyword', - description: 'Contents of the Content-Type: header from the server.\n', - }, - ], - }, - { - name: 'smb_cmd', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek smb_cmd log.\n', - fields: [ - { - name: 'command', - type: 'keyword', - description: 'The command sent by the client.\n', - }, - { - name: 'sub_command', - type: 'keyword', - description: 'The subcommand sent by the client, if present.\n', - }, - { - name: 'argument', - type: 'keyword', - description: 'Command argument sent by the client, if any.\n', - }, - { - name: 'status', - type: 'keyword', - description: "Server reply to the client's command.\n", - }, - { - name: 'rtt', - type: 'double', - description: 'Round trip time from the request to the response.\n', - }, - { - name: 'version', - type: 'keyword', - description: 'Version of SMB for the command.\n', - }, - { - name: 'username', - type: 'keyword', - description: 'Authenticated username, if available.\n', - }, - { - name: 'tree', - type: 'keyword', - description: - 'If this is related to a tree, this is the tree that was used for the current command.\n', - }, - { - name: 'tree_service', - type: 'keyword', - description: 'The type of tree (disk share, printer share, named pipe, etc.).\n', - }, - { - name: 'file', - type: 'group', - description: 'If the command referenced a file, store it here.\n', - fields: [ - { - name: 'name', - type: 'keyword', - description: 'Filename if one was seen.\n', - }, - { - name: 'action', - type: 'keyword', - description: 'Action this log record represents.\n', - }, - { - name: 'uid', - type: 'keyword', - description: 'UID of the referenced file.\n', - }, - { - name: 'host', - type: 'group', - fields: [ - { - name: 'tx', - type: 'ip', - description: 'Address of the transmitting host.\n', - }, - { - name: 'rx', - type: 'ip', - description: 'Address of the receiving host.\n', - }, - ], - }, - ], - }, - { - name: 'smb1_offered_dialects', - type: 'keyword', - description: - 'Present if base/protocols/smb/smb1-main.bro is loaded.\nDialects offered by the client.\n', - }, - { - name: 'smb2_offered_dialects', - type: 'integer', - description: - 'Present if base/protocols/smb/smb2-main.bro is loaded.\nDialects offered by the client.\n', - }, - ], - }, - { - name: 'smb_files', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek SMB Files log.\n', - fields: [ - { - name: 'action', - type: 'keyword', - description: 'Action this log record represents.\n', - }, - { - name: 'fid', - type: 'integer', - description: 'ID referencing this file.\n', - }, - { - name: 'name', - type: 'keyword', - description: 'Filename if one was seen.\n', - }, - { - name: 'path', - type: 'keyword', - description: 'Path pulled from the tree this file was transferred to or from.\n', - }, - { - name: 'previous_name', - type: 'keyword', - description: - "If the rename action was seen, this will be the file's previous name.\n", - }, - { - name: 'size', - type: 'long', - description: 'Byte size of the file.\n', - }, - { - name: 'times', - type: 'group', - description: 'Timestamps of the file.\n', - fields: [ - { - name: 'accessed', - type: 'date', - description: "The file's access time.\n", - }, - { - name: 'changed', - type: 'date', - description: "The file's change time.\n", - }, - { - name: 'created', - type: 'date', - description: "The file's create time.\n", - }, - { - name: 'modified', - type: 'date', - description: "The file's modify time.\n", - }, - ], - }, - { - name: 'uuid', - type: 'keyword', - description: 'UUID referencing this file if DCE/RPC.\n', - }, - ], - }, - { - name: 'smb_mapping', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek SMB_Mapping log.\n', - fields: [ - { - name: 'path', - type: 'keyword', - description: 'Name of the tree path.\n', - }, - { - name: 'service', - type: 'keyword', - description: - 'The type of resource of the tree (disk share, printer share, named pipe, etc.).\n', - }, - { - name: 'native_file_system', - type: 'keyword', - description: 'File system of the tree.\n', - }, - { - name: 'share_type', - type: 'keyword', - description: - 'If this is SMB2, a share type will be included. For SMB1, the type of share\nwill be deduced and included as well.\n', - }, - ], - }, - { - name: 'smtp', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek SMTP log.\n', - fields: [ - { - name: 'transaction_depth', - type: 'integer', - description: - 'A count to represent the depth of this message transaction in a single connection where multiple messages were transferred.\n', - }, - { - name: 'helo', - type: 'keyword', - description: 'Contents of the Helo header.\n', - }, - { - name: 'mail_from', - type: 'keyword', - description: 'Email addresses found in the MAIL FROM header.\n', - }, - { - name: 'rcpt_to', - type: 'keyword', - description: 'Email addresses found in the RCPT TO header.\n', - }, - { - name: 'date', - type: 'date', - description: 'Contents of the Date header.\n', - }, - { - name: 'from', - type: 'keyword', - description: 'Contents of the From header.\n', - }, - { - name: 'to', - type: 'keyword', - description: 'Contents of the To header.\n', - }, - { - name: 'cc', - type: 'keyword', - description: 'Contents of the CC header.\n', - }, - { - name: 'reply_to', - type: 'keyword', - description: 'Contents of the ReplyTo header.\n', - }, - { - name: 'msg_id', - type: 'keyword', - description: 'Contents of the MsgID header.\n', - }, - { - name: 'in_reply_to', - type: 'keyword', - description: 'Contents of the In-Reply-To header.\n', - }, - { - name: 'subject', - type: 'keyword', - description: 'Contents of the Subject header.\n', - }, - { - name: 'x_originating_ip', - type: 'keyword', - description: 'Contents of the X-Originating-IP header.\n', - }, - { - name: 'first_received', - type: 'keyword', - description: 'Contents of the first Received header.\n', - }, - { - name: 'second_received', - type: 'keyword', - description: 'Contents of the second Received header.\n', - }, - { - name: 'last_reply', - type: 'keyword', - description: 'The last message that the server sent to the client.\n', - }, - { - name: 'path', - type: 'ip', - description: 'The message transmission path, as extracted from the headers.\n', - }, - { - name: 'user_agent', - type: 'keyword', - description: 'Value of the User-Agent header from the client.\n', - }, - { - name: 'tls', - type: 'boolean', - description: 'Indicates that the connection has switched to using TLS.\n', - }, - { - name: 'process_received_from', - type: 'boolean', - description: - 'Indicates if the "Received: from" headers should still be processed.\n', - }, - { - name: 'has_client_activity', - type: 'boolean', - description: 'Indicates if client activity has been seen, but not yet logged.\n', - }, - { - name: 'fuids', - type: 'keyword', - description: - '(present if base/protocols/smtp/files.bro is loaded)\nAn ordered vector of file unique IDs seen attached to the message.\n', - }, - { - name: 'is_webmail', - type: 'boolean', - description: 'Indicates if the message was sent through a webmail interface.\n', - }, - ], - }, - { - name: 'snmp', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek SNMP log.\n', - fields: [ - { - name: 'duration', - type: 'double', - description: - 'The amount of time between the first packet beloning to the SNMP session and the latest one seen.\n', - }, - { - name: 'version', - type: 'keyword', - description: 'The version of SNMP being used.\n', - }, - { - name: 'community', - type: 'keyword', - description: - "The community string of the first SNMP packet associated with the session. This is used as part of SNMP's (v1 and v2c) administrative/security framework. See RFC 1157 or RFC 1901.\n", - }, - { - name: 'get', - type: 'group', - fields: [ - { - name: 'requests', - type: 'integer', - description: - 'The number of variable bindings in GetRequest/GetNextRequest PDUs seen for the session.\n', - }, - { - name: 'bulk_requests', - type: 'integer', - description: - 'The number of variable bindings in GetBulkRequest PDUs seen for the session.\n', - }, - { - name: 'responses', - type: 'integer', - description: - 'The number of variable bindings in GetResponse/Response PDUs seen for the session.\n', - }, - ], - }, - { - name: 'set', - type: 'group', - fields: [ - { - name: 'requests', - type: 'integer', - description: - 'The number of variable bindings in SetRequest PDUs seen for the session.\n', - }, - ], - }, - { - name: 'display_string', - type: 'keyword', - description: 'A system description of the SNMP responder endpoint.\n', - }, - { - name: 'up_since', - type: 'date', - description: - "The time at which the SNMP responder endpoint claims it's been up since.\n", - }, - ], - }, - { - name: 'socks', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek SOCKS log.\n', - fields: [ - { - name: 'version', - type: 'integer', - description: 'Protocol version of SOCKS.\n', - }, - { - name: 'user', - type: 'keyword', - description: 'Username used to request a login to the proxy.\n', - }, - { - name: 'password', - type: 'keyword', - description: 'Password used to request a login to the proxy.\n', - }, - { - name: 'status', - type: 'keyword', - description: 'Server status for the attempt at using the proxy.\n', - }, - { - name: 'request', - type: 'group', - fields: [ - { - name: 'host', - type: 'keyword', - description: - 'Client requested SOCKS address. Could be an address, a name or both.\n', - }, - { - name: 'port', - type: 'integer', - description: 'Client requested port.\n', - }, - ], - }, - { - name: 'bound', - type: 'group', - fields: [ - { - name: 'host', - type: 'keyword', - description: 'Server bound address. Could be an address, a name or both.\n', - }, - { - name: 'port', - type: 'integer', - description: 'Server bound port.\n', - }, - ], - }, - { - name: 'capture_password', - type: 'boolean', - description: 'Determines if the password will be captured for this request.\n', - }, - ], - }, - { - name: 'ssh', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek SSH log.\n', - fields: [ - { - name: 'client', - type: 'keyword', - description: "The client's version string.\n", - }, - { - name: 'direction', - type: 'keyword', - description: - 'Direction of the connection. If the client was a local host logging into\nan external host, this would be OUTBOUND. INBOUND would be set for the\nopposite situation.\n', - }, - { - name: 'host_key', - type: 'keyword', - description: "The server's key thumbprint.\n", - }, - { - name: 'server', - type: 'keyword', - description: "The server's version string.\n", - }, - { - name: 'version', - type: 'integer', - description: 'SSH major version (1 or 2).\n', - }, - { - name: 'algorithm', - type: 'group', - description: 'Cipher algorithms used in this session.\n', - fields: [ - { - name: 'cipher', - type: 'keyword', - description: 'The encryption algorithm in use.\n', - }, - { - name: 'compression', - type: 'keyword', - description: 'The compression algorithm in use.\n', - }, - { - name: 'host_key', - type: 'keyword', - description: "The server host key's algorithm.\n", - }, - { - name: 'key_exchange', - type: 'keyword', - description: 'The key exchange algorithm in use.\n', - }, - { - name: 'mac', - type: 'keyword', - description: 'The signing (MAC) algorithm in use.\n', - }, - ], - }, - { - name: 'auth', - type: 'group', - fields: [ - { - name: 'attempts', - type: 'integer', - description: - "The number of authentication attemps we observed. There's always at\nleast one, since some servers might support no authentication at all.\nIt's important to note that not all of these are failures, since some\nservers require two-factor auth (e.g. password AND pubkey).\n", - }, - { - name: 'success', - type: 'boolean', - description: 'Authentication result.\n', - }, - ], - }, - ], - }, - { - name: 'ssl', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek SSL log.\n', - fields: [ - { - name: 'version', - type: 'keyword', - description: 'SSL/TLS version that was logged.\n', - }, - { - name: 'cipher', - type: 'keyword', - description: 'SSL/TLS cipher suite that was logged.\n', - }, - { - name: 'curve', - type: 'keyword', - description: 'Elliptic curve that was logged when using ECDH/ECDHE.\n', - }, - { - name: 'resumed', - type: 'boolean', - description: - 'Flag to indicate if the session was resumed reusing the key material exchanged in an\nearlier connection.\n', - }, - { - name: 'next_protocol', - type: 'keyword', - description: - 'Next protocol the server chose using the application layer next protocol extension.\n', - }, - { - name: 'established', - type: 'boolean', - description: - 'Flag to indicate if this ssl session has been established successfully.\n', - }, - { - name: 'validation', - type: 'group', - fields: [ - { - name: 'status', - type: 'keyword', - description: 'Result of certificate validation for this connection.\n', - }, - { - name: 'code', - type: 'keyword', - description: - 'Result of certificate validation for this connection, given as OpenSSL validation code.\n', - }, - ], - }, - { - name: 'last_alert', - type: 'keyword', - description: 'Last alert that was seen during the connection.\n', - }, - { - name: 'server', - type: 'group', - fields: [ - { - name: 'name', - type: 'keyword', - description: - 'Value of the Server Name Indicator SSL/TLS extension. It indicates the server name\nthat the client was requesting.\n', - }, - { - name: 'cert_chain', - type: 'keyword', - description: - 'Chain of certificates offered by the server to validate its complete signing chain.\n', - }, - { - name: 'cert_chain_fuids', - type: 'keyword', - description: - 'An ordered vector of certificate file identifiers for the certificates offered by the server.\n', - }, - { - name: 'issuer', - type: 'group', - description: - 'Subject of the signer of the X.509 certificate offered by the server.\n', - fields: [ - { - name: 'common_name', - type: 'keyword', - description: - 'Common name of the signer of the X.509 certificate offered by the server.\n', - }, - { - name: 'country', - type: 'keyword', - description: - 'Country code of the signer of the X.509 certificate offered by the server.\n', - }, - { - name: 'locality', - type: 'keyword', - description: - 'Locality of the signer of the X.509 certificate offered by the server.\n', - }, - { - name: 'organization', - type: 'keyword', - description: - 'Organization of the signer of the X.509 certificate offered by the server.\n', - }, - { - name: 'organizational_unit', - type: 'keyword', - description: - 'Organizational unit of the signer of the X.509 certificate offered by the server.\n', - }, - { - name: 'state', - type: 'keyword', - description: - 'State or province name of the signer of the X.509 certificate offered by the server.\n', - }, - ], - }, - { - name: 'subject', - type: 'group', - description: 'Subject of the X.509 certificate offered by the server.\n', - fields: [ - { - name: 'common_name', - type: 'keyword', - description: - 'Common name of the X.509 certificate offered by the server.\n', - }, - { - name: 'country', - type: 'keyword', - description: - 'Country code of the X.509 certificate offered by the server.\n', - }, - { - name: 'locality', - type: 'keyword', - description: 'Locality of the X.509 certificate offered by the server.\n', - }, - { - name: 'organization', - type: 'keyword', - description: - 'Organization of the X.509 certificate offered by the server.\n', - }, - { - name: 'organizational_unit', - type: 'keyword', - description: - 'Organizational unit of the X.509 certificate offered by the server.\n', - }, - { - name: 'state', - type: 'keyword', - description: - 'State or province name of the X.509 certificate offered by the server.\n', - }, - ], - }, - ], - }, - { - name: 'client', - type: 'group', - fields: [ - { - name: 'cert_chain', - type: 'keyword', - description: - 'Chain of certificates offered by the client to validate its complete signing chain.\n', - }, - { - name: 'cert_chain_fuids', - type: 'keyword', - description: - 'An ordered vector of certificate file identifiers for the certificates offered by the client.\n', - }, - { - name: 'issuer', - type: 'group', - description: - 'Subject of the signer of the X.509 certificate offered by the client.\n', - fields: [ - { - name: 'common_name', - type: 'keyword', - description: - 'Common name of the signer of the X.509 certificate offered by the client.\n', - }, - { - name: 'country', - type: 'keyword', - description: - 'Country code of the signer of the X.509 certificate offered by the client.\n', - }, - { - name: 'locality', - type: 'keyword', - description: - 'Locality of the signer of the X.509 certificate offered by the client.\n', - }, - { - name: 'organization', - type: 'keyword', - description: - 'Organization of the signer of the X.509 certificate offered by the client.\n', - }, - { - name: 'organizational_unit', - type: 'keyword', - description: - 'Organizational unit of the signer of the X.509 certificate offered by the client.\n', - }, - { - name: 'state', - type: 'keyword', - description: - 'State or province name of the signer of the X.509 certificate offered by the client.\n', - }, - ], - }, - { - name: 'subject', - type: 'group', - description: 'Subject of the X.509 certificate offered by the client.\n', - fields: [ - { - name: 'common_name', - type: 'keyword', - description: - 'Common name of the X.509 certificate offered by the client.\n', - }, - { - name: 'country', - type: 'keyword', - description: - 'Country code of the X.509 certificate offered by the client.\n', - }, - { - name: 'locality', - type: 'keyword', - description: 'Locality of the X.509 certificate offered by the client.\n', - }, - { - name: 'organization', - type: 'keyword', - description: - 'Organization of the X.509 certificate offered by the client.\n', - }, - { - name: 'organizational_unit', - type: 'keyword', - description: - 'Organizational unit of the X.509 certificate offered by the client.\n', - }, - { - name: 'state', - type: 'keyword', - description: - 'State or province name of the X.509 certificate offered by the client.\n', - }, - ], - }, - ], - }, - ], - }, - { - name: 'stats', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek stats log.\n', - fields: [ - { - name: 'peer', - type: 'keyword', - description: 'Peer that generated this log. Mostly for clusters.\n', - }, - { - name: 'memory', - type: 'integer', - description: 'Amount of memory currently in use in MB.\n', - }, - { - name: 'packets', - type: 'group', - fields: [ - { - name: 'processed', - type: 'long', - description: 'Number of packets processed since the last stats interval.\n', - }, - { - name: 'dropped', - type: 'long', - description: - 'Number of packets dropped since the last stats interval if reading live traffic.\n', - }, - { - name: 'received', - type: 'long', - description: - 'Number of packets seen on the link since the last stats interval if reading live traffic.\n', - }, - ], - }, - { - name: 'bytes', - type: 'group', - fields: [ - { - name: 'received', - type: 'long', - description: - 'Number of bytes received since the last stats interval if reading live traffic.\n', - }, - ], - }, - { - name: 'connections', - type: 'group', - fields: [ - { - name: 'tcp', - type: 'group', - fields: [ - { - name: 'active', - type: 'integer', - description: 'TCP connections currently in memory.\n', - }, - { - name: 'count', - type: 'integer', - description: 'TCP connections seen since last stats interval.\n', - }, - ], - }, - { - name: 'udp', - type: 'group', - fields: [ - { - name: 'active', - type: 'integer', - description: 'UDP connections currently in memory.\n', - }, - { - name: 'count', - type: 'integer', - description: 'UDP connections seen since last stats interval.\n', - }, - ], - }, - { - name: 'icmp', - type: 'group', - fields: [ - { - name: 'active', - type: 'integer', - description: 'ICMP connections currently in memory.\n', - }, - { - name: 'count', - type: 'integer', - description: 'ICMP connections seen since last stats interval.\n', - }, - ], - }, - ], - }, - { - name: 'events', - type: 'group', - fields: [ - { - name: 'processed', - type: 'integer', - description: 'Number of events processed since the last stats interval.\n', - }, - { - name: 'queued', - type: 'integer', - description: - 'Number of events that have been queued since the last stats interval.\n', - }, - ], - }, - { - name: 'timers', - type: 'group', - fields: [ - { - name: 'count', - type: 'integer', - description: 'Number of timers scheduled since last stats interval.\n', - }, - { - name: 'active', - type: 'integer', - description: 'Current number of scheduled timers.\n', - }, - ], - }, - { - name: 'files', - type: 'group', - fields: [ - { - name: 'count', - type: 'integer', - description: 'Number of files seen since last stats interval.\n', - }, - { - name: 'active', - type: 'integer', - description: 'Current number of files actively being seen.\n', - }, - ], - }, - { - name: 'dns_requests', - type: 'group', - fields: [ - { - name: 'count', - type: 'integer', - description: 'Number of DNS requests seen since last stats interval.\n', - }, - { - name: 'active', - type: 'integer', - description: 'Current number of DNS requests awaiting a reply.\n', - }, - ], - }, - { - name: 'reassembly_size', - type: 'group', - fields: [ - { - name: 'tcp', - type: 'integer', - description: 'Current size of TCP data in reassembly.\n', - }, - { - name: 'file', - type: 'integer', - description: 'Current size of File data in reassembly.\n', - }, - { - name: 'frag', - type: 'integer', - description: 'Current size of packet fragment data in reassembly.\n', - }, - { - name: 'unknown', - type: 'integer', - description: - 'Current size of unknown data in reassembly (this is only PIA buffer right now).\n', - }, - ], - }, - { - name: 'timestamp_lag', - type: 'integer', - description: - 'Lag between the wall clock and packet timestamps if reading live traffic.\n', - }, - ], - }, - { - name: 'syslog', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek syslog log.\n', - fields: [ - { - name: 'facility', - type: 'keyword', - description: 'Syslog facility for the message.\n', - }, - { - name: 'severity', - type: 'keyword', - description: 'Syslog severity for the message.\n', - }, - { - name: 'message', - type: 'keyword', - description: 'The plain text message.\n', - }, - ], - }, - { - name: 'tunnel', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek SSH log.\n', - fields: [ - { - name: 'type', - type: 'keyword', - description: 'The type of tunnel.\n', - }, - { - name: 'action', - type: 'keyword', - description: 'The type of activity that occurred.\n', - }, - ], - }, - { - name: 'weird', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek Weird log.\n', - fields: [ - { - name: 'name', - type: 'keyword', - description: 'The name of the weird that occurred.\n', - }, - { - name: 'additional_info', - type: 'keyword', - description: 'Additional information accompanying the weird if any.\n', - }, - { - name: 'notice', - type: 'boolean', - description: 'Indicate if this weird was also turned into a notice.\n', - }, - { - name: 'peer', - type: 'keyword', - description: - 'The peer that originated this weird. This is helpful in cluster deployments if a particular cluster node is having trouble to help identify which node is having trouble.\n', - }, - { - name: 'identifier', - type: 'keyword', - description: - 'This field is to be provided when a weird is generated for the purpose of deduplicating weirds. The identifier string should be unique for a single instance of the weird. This field is used to define when a weird is conceptually a duplicate of a previous weird.\n', - }, - ], - }, - { - name: 'x509', - type: 'group', - default_field: false, - description: 'Fields exported by the Zeek x509 log.\n', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'File id of this certificate.\n', - }, - { - name: 'certificate', - type: 'group', - description: 'Basic information about the certificate.\n', - fields: [ - { - name: 'version', - type: 'integer', - description: 'Version number.\n', - }, - { - name: 'serial', - type: 'keyword', - description: 'Serial number.\n', - }, - { - name: 'subject', - type: 'group', - description: 'Subject.\n', - fields: [ - { - name: 'country', - type: 'keyword', - description: 'Country provided in the certificate subject.\n', - }, - { - name: 'common_name', - type: 'keyword', - description: 'Common name provided in the certificate subject.\n', - }, - { - name: 'locality', - type: 'keyword', - description: 'Locality provided in the certificate subject.\n', - }, - { - name: 'organization', - type: 'keyword', - description: 'Organization provided in the certificate subject.\n', - }, - { - name: 'organizational_unit', - type: 'keyword', - description: 'Organizational unit provided in the certificate subject.\n', - }, - { - name: 'state', - type: 'keyword', - description: 'State or province provided in the certificate subject.\n', - }, - ], - }, - { - name: 'issuer', - type: 'group', - description: 'Issuer.\n', - fields: [ - { - name: 'country', - type: 'keyword', - description: 'Country provided in the certificate issuer field.\n', - }, - { - name: 'common_name', - type: 'keyword', - description: 'Common name provided in the certificate issuer field.\n', - }, - { - name: 'locality', - type: 'keyword', - description: 'Locality provided in the certificate issuer field.\n', - }, - { - name: 'organization', - type: 'keyword', - description: 'Organization provided in the certificate issuer field.\n', - }, - { - name: 'organizational_unit', - type: 'keyword', - description: - 'Organizational unit provided in the certificate issuer field.\n', - }, - { - name: 'state', - type: 'keyword', - description: - 'State or province provided in the certificate issuer field.\n', - }, - ], - }, - { - name: 'common_name', - type: 'keyword', - description: 'Last (most specific) common name.\n', - }, - { - name: 'valid', - type: 'group', - description: 'Certificate validity timestamps\n', - fields: [ - { - name: 'from', - type: 'date', - description: 'Timestamp before when certificate is not valid.\n', - }, - { - name: 'until', - type: 'date', - description: 'Timestamp after when certificate is not valid.\n', - }, - ], - }, - { - name: 'key', - type: 'group', - fields: [ - { - name: 'algorithm', - type: 'keyword', - description: 'Name of the key algorithm.\n', - }, - { - name: 'type', - type: 'keyword', - description: - 'Key type, if key parseable by openssl (either rsa, dsa or ec).\n', - }, - { - name: 'length', - type: 'integer', - description: 'Key length in bits.\n', - }, - ], - }, - { - name: 'signature_algorithm', - type: 'keyword', - description: 'Name of the signature algorithm.\n', - }, - { - name: 'exponent', - type: 'keyword', - description: 'Exponent, if RSA-certificate.\n', - }, - { - name: 'curve', - type: 'keyword', - description: 'Curve, if EC-certificate.\n', - }, - ], - }, - { - name: 'san', - type: 'group', - description: 'Subject alternative name extension of the certificate.\n', - fields: [ - { - name: 'dns', - type: 'keyword', - description: 'List of DNS entries in SAN.\n', - }, - { - name: 'uri', - type: 'keyword', - description: 'List of URI entries in SAN.\n', - }, - { - name: 'email', - type: 'keyword', - description: 'List of email entries in SAN.\n', - }, - { - name: 'ip', - type: 'ip', - description: 'List of IP entries in SAN.\n', - }, - { - name: 'other_fields', - type: 'boolean', - description: - 'True if the certificate contained other, not recognized or parsed name fields.\n', - }, - ], - }, - { - name: 'basic_constraints', - type: 'group', - description: 'Basic constraints extension of the certificate.\n', - fields: [ - { - name: 'certificate_authority', - type: 'boolean', - description: 'CA flag set or not.\n', - }, - { - name: 'path_length', - type: 'integer', - description: 'Maximum path length.\n', - }, - ], - }, - { - name: 'log_cert', - type: 'boolean', - description: - 'Present if policy/protocols/ssl/log-hostcerts-only.bro is loaded\nLogging of certificate is suppressed if set to F.\n', - }, - ], - }, - ], - }, - ], - }, - { - key: 'netflow', - title: 'NetFlow', - description: 'Fields from NetFlow and IPFIX flows.\n', - fields: [ - { - name: 'netflow', - type: 'group', - description: 'Fields from NetFlow and IPFIX.\n', - fields: [ - { - name: 'type', - type: 'keyword', - description: 'The type of NetFlow record described by this event.\n', - }, - { - name: 'exporter', - type: 'group', - description: 'Metadata related to the exporter device that generated this record.\n', - fields: [ - { - name: 'address', - type: 'keyword', - description: "Exporter's network address in IP:port format.\n", - }, - { - name: 'source_id', - type: 'long', - description: 'Observation domain ID to which this record belongs.\n', - }, - { - name: 'timestamp', - type: 'date', - description: 'Time and date of export.\n', - }, - { - name: 'uptime_millis', - type: 'long', - description: 'How long the exporter process has been running, in milliseconds.\n', - }, - { - name: 'version', - type: 'integer', - description: 'NetFlow version used.\n', - }, - ], - }, - { - name: 'octet_delta_count', - type: 'long', - }, - { - name: 'packet_delta_count', - type: 'long', - }, - { - name: 'delta_flow_count', - type: 'long', - }, - { - name: 'protocol_identifier', - type: 'short', - }, - { - name: 'ip_class_of_service', - type: 'short', - }, - { - name: 'tcp_control_bits', - type: 'integer', - }, - { - name: 'source_transport_port', - type: 'integer', - }, - { - name: 'source_ipv4_address', - type: 'ip', - }, - { - name: 'source_ipv4_prefix_length', - type: 'short', - }, - { - name: 'ingress_interface', - type: 'long', - }, - { - name: 'destination_transport_port', - type: 'integer', - }, - { - name: 'destination_ipv4_address', - type: 'ip', - }, - { - name: 'destination_ipv4_prefix_length', - type: 'short', - }, - { - name: 'egress_interface', - type: 'long', - }, - { - name: 'ip_next_hop_ipv4_address', - type: 'ip', - }, - { - name: 'bgp_source_as_number', - type: 'long', - }, - { - name: 'bgp_destination_as_number', - type: 'long', - }, - { - name: 'bgp_next_hop_ipv4_address', - type: 'ip', - }, - { - name: 'post_mcast_packet_delta_count', - type: 'long', - }, - { - name: 'post_mcast_octet_delta_count', - type: 'long', - }, - { - name: 'flow_end_sys_up_time', - type: 'long', - }, - { - name: 'flow_start_sys_up_time', - type: 'long', - }, - { - name: 'post_octet_delta_count', - type: 'long', - }, - { - name: 'post_packet_delta_count', - type: 'long', - }, - { - name: 'minimum_ip_total_length', - type: 'long', - }, - { - name: 'maximum_ip_total_length', - type: 'long', - }, - { - name: 'source_ipv6_address', - type: 'ip', - }, - { - name: 'destination_ipv6_address', - type: 'ip', - }, - { - name: 'source_ipv6_prefix_length', - type: 'short', - }, - { - name: 'destination_ipv6_prefix_length', - type: 'short', - }, - { - name: 'flow_label_ipv6', - type: 'long', - }, - { - name: 'icmp_type_code_ipv4', - type: 'integer', - }, - { - name: 'igmp_type', - type: 'short', - }, - { - name: 'sampling_interval', - type: 'long', - }, - { - name: 'sampling_algorithm', - type: 'short', - }, - { - name: 'flow_active_timeout', - type: 'integer', - }, - { - name: 'flow_idle_timeout', - type: 'integer', - }, - { - name: 'engine_type', - type: 'short', - }, - { - name: 'engine_id', - type: 'short', - }, - { - name: 'exported_octet_total_count', - type: 'long', - }, - { - name: 'exported_message_total_count', - type: 'long', - }, - { - name: 'exported_flow_record_total_count', - type: 'long', - }, - { - name: 'ipv4_router_sc', - type: 'ip', - }, - { - name: 'source_ipv4_prefix', - type: 'ip', - }, - { - name: 'destination_ipv4_prefix', - type: 'ip', - }, - { - name: 'mpls_top_label_type', - type: 'short', - }, - { - name: 'mpls_top_label_ipv4_address', - type: 'ip', - }, - { - name: 'sampler_id', - type: 'short', - }, - { - name: 'sampler_mode', - type: 'short', - }, - { - name: 'sampler_random_interval', - type: 'long', - }, - { - name: 'class_id', - type: 'long', - }, - { - name: 'minimum_ttl', - type: 'short', - }, - { - name: 'maximum_ttl', - type: 'short', - }, - { - name: 'fragment_identification', - type: 'long', - }, - { - name: 'post_ip_class_of_service', - type: 'short', - }, - { - name: 'source_mac_address', - type: 'keyword', - }, - { - name: 'post_destination_mac_address', - type: 'keyword', - }, - { - name: 'vlan_id', - type: 'integer', - }, - { - name: 'post_vlan_id', - type: 'integer', - }, - { - name: 'ip_version', - type: 'short', - }, - { - name: 'flow_direction', - type: 'short', - }, - { - name: 'ip_next_hop_ipv6_address', - type: 'ip', - }, - { - name: 'bgp_next_hop_ipv6_address', - type: 'ip', - }, - { - name: 'ipv6_extension_headers', - type: 'long', - }, - { - name: 'mpls_top_label_stack_section', - type: 'short', - }, - { - name: 'mpls_label_stack_section2', - type: 'short', - }, - { - name: 'mpls_label_stack_section3', - type: 'short', - }, - { - name: 'mpls_label_stack_section4', - type: 'short', - }, - { - name: 'mpls_label_stack_section5', - type: 'short', - }, - { - name: 'mpls_label_stack_section6', - type: 'short', - }, - { - name: 'mpls_label_stack_section7', - type: 'short', - }, - { - name: 'mpls_label_stack_section8', - type: 'short', - }, - { - name: 'mpls_label_stack_section9', - type: 'short', - }, - { - name: 'mpls_label_stack_section10', - type: 'short', - }, - { - name: 'destination_mac_address', - type: 'keyword', - }, - { - name: 'post_source_mac_address', - type: 'keyword', - }, - { - name: 'interface_name', - type: 'keyword', - }, - { - name: 'interface_description', - type: 'keyword', - }, - { - name: 'sampler_name', - type: 'keyword', - }, - { - name: 'octet_total_count', - type: 'long', - }, - { - name: 'packet_total_count', - type: 'long', - }, - { - name: 'flags_and_sampler_id', - type: 'long', - }, - { - name: 'fragment_offset', - type: 'integer', - }, - { - name: 'forwarding_status', - type: 'short', - }, - { - name: 'mpls_vpn_route_distinguisher', - type: 'short', - }, - { - name: 'mpls_top_label_prefix_length', - type: 'short', - }, - { - name: 'src_traffic_index', - type: 'long', - }, - { - name: 'dst_traffic_index', - type: 'long', - }, - { - name: 'application_description', - type: 'keyword', - }, - { - name: 'application_id', - type: 'short', - }, - { - name: 'application_name', - type: 'keyword', - }, - { - name: 'post_ip_diff_serv_code_point', - type: 'short', - }, - { - name: 'multicast_replication_factor', - type: 'long', - }, - { - name: 'class_name', - type: 'keyword', - }, - { - name: 'classification_engine_id', - type: 'short', - }, - { - name: 'layer2packet_section_offset', - type: 'integer', - }, - { - name: 'layer2packet_section_size', - type: 'integer', - }, - { - name: 'layer2packet_section_data', - type: 'short', - }, - { - name: 'bgp_next_adjacent_as_number', - type: 'long', - }, - { - name: 'bgp_prev_adjacent_as_number', - type: 'long', - }, - { - name: 'exporter_ipv4_address', - type: 'ip', - }, - { - name: 'exporter_ipv6_address', - type: 'ip', - }, - { - name: 'dropped_octet_delta_count', - type: 'long', - }, - { - name: 'dropped_packet_delta_count', - type: 'long', - }, - { - name: 'dropped_octet_total_count', - type: 'long', - }, - { - name: 'dropped_packet_total_count', - type: 'long', - }, - { - name: 'flow_end_reason', - type: 'short', - }, - { - name: 'common_properties_id', - type: 'long', - }, - { - name: 'observation_point_id', - type: 'long', - }, - { - name: 'icmp_type_code_ipv6', - type: 'integer', - }, - { - name: 'mpls_top_label_ipv6_address', - type: 'ip', - }, - { - name: 'line_card_id', - type: 'long', - }, - { - name: 'port_id', - type: 'long', - }, - { - name: 'metering_process_id', - type: 'long', - }, - { - name: 'exporting_process_id', - type: 'long', - }, - { - name: 'template_id', - type: 'integer', - }, - { - name: 'wlan_channel_id', - type: 'short', - }, - { - name: 'wlan_ssid', - type: 'keyword', - }, - { - name: 'flow_id', - type: 'long', - }, - { - name: 'observation_domain_id', - type: 'long', - }, - { - name: 'flow_start_seconds', - type: 'date', - }, - { - name: 'flow_end_seconds', - type: 'date', - }, - { - name: 'flow_start_milliseconds', - type: 'date', - }, - { - name: 'flow_end_milliseconds', - type: 'date', - }, - { - name: 'flow_start_microseconds', - type: 'date', - }, - { - name: 'flow_end_microseconds', - type: 'date', - }, - { - name: 'flow_start_nanoseconds', - type: 'date', - }, - { - name: 'flow_end_nanoseconds', - type: 'date', - }, - { - name: 'flow_start_delta_microseconds', - type: 'long', - }, - { - name: 'flow_end_delta_microseconds', - type: 'long', - }, - { - name: 'system_init_time_milliseconds', - type: 'date', - }, - { - name: 'flow_duration_milliseconds', - type: 'long', - }, - { - name: 'flow_duration_microseconds', - type: 'long', - }, - { - name: 'observed_flow_total_count', - type: 'long', - }, - { - name: 'ignored_packet_total_count', - type: 'long', - }, - { - name: 'ignored_octet_total_count', - type: 'long', - }, - { - name: 'not_sent_flow_total_count', - type: 'long', - }, - { - name: 'not_sent_packet_total_count', - type: 'long', - }, - { - name: 'not_sent_octet_total_count', - type: 'long', - }, - { - name: 'destination_ipv6_prefix', - type: 'ip', - }, - { - name: 'source_ipv6_prefix', - type: 'ip', - }, - { - name: 'post_octet_total_count', - type: 'long', - }, - { - name: 'post_packet_total_count', - type: 'long', - }, - { - name: 'flow_key_indicator', - type: 'long', - }, - { - name: 'post_mcast_packet_total_count', - type: 'long', - }, - { - name: 'post_mcast_octet_total_count', - type: 'long', - }, - { - name: 'icmp_type_ipv4', - type: 'short', - }, - { - name: 'icmp_code_ipv4', - type: 'short', - }, - { - name: 'icmp_type_ipv6', - type: 'short', - }, - { - name: 'icmp_code_ipv6', - type: 'short', - }, - { - name: 'udp_source_port', - type: 'integer', - }, - { - name: 'udp_destination_port', - type: 'integer', - }, - { - name: 'tcp_source_port', - type: 'integer', - }, - { - name: 'tcp_destination_port', - type: 'integer', - }, - { - name: 'tcp_sequence_number', - type: 'long', - }, - { - name: 'tcp_acknowledgement_number', - type: 'long', - }, - { - name: 'tcp_window_size', - type: 'integer', - }, - { - name: 'tcp_urgent_pointer', - type: 'integer', - }, - { - name: 'tcp_header_length', - type: 'short', - }, - { - name: 'ip_header_length', - type: 'short', - }, - { - name: 'total_length_ipv4', - type: 'integer', - }, - { - name: 'payload_length_ipv6', - type: 'integer', - }, - { - name: 'ip_ttl', - type: 'short', - }, - { - name: 'next_header_ipv6', - type: 'short', - }, - { - name: 'mpls_payload_length', - type: 'long', - }, - { - name: 'ip_diff_serv_code_point', - type: 'short', - }, - { - name: 'ip_precedence', - type: 'short', - }, - { - name: 'fragment_flags', - type: 'short', - }, - { - name: 'octet_delta_sum_of_squares', - type: 'long', - }, - { - name: 'octet_total_sum_of_squares', - type: 'long', - }, - { - name: 'mpls_top_label_ttl', - type: 'short', - }, - { - name: 'mpls_label_stack_length', - type: 'long', - }, - { - name: 'mpls_label_stack_depth', - type: 'long', - }, - { - name: 'mpls_top_label_exp', - type: 'short', - }, - { - name: 'ip_payload_length', - type: 'long', - }, - { - name: 'udp_message_length', - type: 'integer', - }, - { - name: 'is_multicast', - type: 'short', - }, - { - name: 'ipv4_ihl', - type: 'short', - }, - { - name: 'ipv4_options', - type: 'long', - }, - { - name: 'tcp_options', - type: 'long', - }, - { - name: 'padding_octets', - type: 'short', - }, - { - name: 'collector_ipv4_address', - type: 'ip', - }, - { - name: 'collector_ipv6_address', - type: 'ip', - }, - { - name: 'export_interface', - type: 'long', - }, - { - name: 'export_protocol_version', - type: 'short', - }, - { - name: 'export_transport_protocol', - type: 'short', - }, - { - name: 'collector_transport_port', - type: 'integer', - }, - { - name: 'exporter_transport_port', - type: 'integer', - }, - { - name: 'tcp_syn_total_count', - type: 'long', - }, - { - name: 'tcp_fin_total_count', - type: 'long', - }, - { - name: 'tcp_rst_total_count', - type: 'long', - }, - { - name: 'tcp_psh_total_count', - type: 'long', - }, - { - name: 'tcp_ack_total_count', - type: 'long', - }, - { - name: 'tcp_urg_total_count', - type: 'long', - }, - { - name: 'ip_total_length', - type: 'long', - }, - { - name: 'post_nat_source_ipv4_address', - type: 'ip', - }, - { - name: 'post_nat_destination_ipv4_address', - type: 'ip', - }, - { - name: 'post_napt_source_transport_port', - type: 'integer', - }, - { - name: 'post_napt_destination_transport_port', - type: 'integer', - }, - { - name: 'nat_originating_address_realm', - type: 'short', - }, - { - name: 'nat_event', - type: 'short', - }, - { - name: 'initiator_octets', - type: 'long', - }, - { - name: 'responder_octets', - type: 'long', - }, - { - name: 'firewall_event', - type: 'short', - }, - { - name: 'ingress_vrfid', - type: 'long', - }, - { - name: 'egress_vrfid', - type: 'long', - }, - { - name: 'vr_fname', - type: 'keyword', - }, - { - name: 'post_mpls_top_label_exp', - type: 'short', - }, - { - name: 'tcp_window_scale', - type: 'integer', - }, - { - name: 'biflow_direction', - type: 'short', - }, - { - name: 'ethernet_header_length', - type: 'short', - }, - { - name: 'ethernet_payload_length', - type: 'integer', - }, - { - name: 'ethernet_total_length', - type: 'integer', - }, - { - name: 'dot1q_vlan_id', - type: 'integer', - }, - { - name: 'dot1q_priority', - type: 'short', - }, - { - name: 'dot1q_customer_vlan_id', - type: 'integer', - }, - { - name: 'dot1q_customer_priority', - type: 'short', - }, - { - name: 'metro_evc_id', - type: 'keyword', - }, - { - name: 'metro_evc_type', - type: 'short', - }, - { - name: 'pseudo_wire_id', - type: 'long', - }, - { - name: 'pseudo_wire_type', - type: 'integer', - }, - { - name: 'pseudo_wire_control_word', - type: 'long', - }, - { - name: 'ingress_physical_interface', - type: 'long', - }, - { - name: 'egress_physical_interface', - type: 'long', - }, - { - name: 'post_dot1q_vlan_id', - type: 'integer', - }, - { - name: 'post_dot1q_customer_vlan_id', - type: 'integer', - }, - { - name: 'ethernet_type', - type: 'integer', - }, - { - name: 'post_ip_precedence', - type: 'short', - }, - { - name: 'collection_time_milliseconds', - type: 'date', - }, - { - name: 'export_sctp_stream_id', - type: 'integer', - }, - { - name: 'max_export_seconds', - type: 'date', - }, - { - name: 'max_flow_end_seconds', - type: 'date', - }, - { - name: 'message_md5_checksum', - type: 'short', - }, - { - name: 'message_scope', - type: 'short', - }, - { - name: 'min_export_seconds', - type: 'date', - }, - { - name: 'min_flow_start_seconds', - type: 'date', - }, - { - name: 'opaque_octets', - type: 'short', - }, - { - name: 'session_scope', - type: 'short', - }, - { - name: 'max_flow_end_microseconds', - type: 'date', - }, - { - name: 'max_flow_end_milliseconds', - type: 'date', - }, - { - name: 'max_flow_end_nanoseconds', - type: 'date', - }, - { - name: 'min_flow_start_microseconds', - type: 'date', - }, - { - name: 'min_flow_start_milliseconds', - type: 'date', - }, - { - name: 'min_flow_start_nanoseconds', - type: 'date', - }, - { - name: 'collector_certificate', - type: 'short', - }, - { - name: 'exporter_certificate', - type: 'short', - }, - { - name: 'data_records_reliability', - type: 'boolean', - }, - { - name: 'observation_point_type', - type: 'short', - }, - { - name: 'new_connection_delta_count', - type: 'long', - }, - { - name: 'connection_sum_duration_seconds', - type: 'long', - }, - { - name: 'connection_transaction_id', - type: 'long', - }, - { - name: 'post_nat_source_ipv6_address', - type: 'ip', - }, - { - name: 'post_nat_destination_ipv6_address', - type: 'ip', - }, - { - name: 'nat_pool_id', - type: 'long', - }, - { - name: 'nat_pool_name', - type: 'keyword', - }, - { - name: 'anonymization_flags', - type: 'integer', - }, - { - name: 'anonymization_technique', - type: 'integer', - }, - { - name: 'information_element_index', - type: 'integer', - }, - { - name: 'p2p_technology', - type: 'keyword', - }, - { - name: 'tunnel_technology', - type: 'keyword', - }, - { - name: 'encrypted_technology', - type: 'keyword', - }, - { - name: 'bgp_validity_state', - type: 'short', - }, - { - name: 'ip_sec_spi', - type: 'long', - }, - { - name: 'gre_key', - type: 'long', - }, - { - name: 'nat_type', - type: 'short', - }, - { - name: 'initiator_packets', - type: 'long', - }, - { - name: 'responder_packets', - type: 'long', - }, - { - name: 'observation_domain_name', - type: 'keyword', - }, - { - name: 'selection_sequence_id', - type: 'long', - }, - { - name: 'selector_id', - type: 'long', - }, - { - name: 'information_element_id', - type: 'integer', - }, - { - name: 'selector_algorithm', - type: 'integer', - }, - { - name: 'sampling_packet_interval', - type: 'long', - }, - { - name: 'sampling_packet_space', - type: 'long', - }, - { - name: 'sampling_time_interval', - type: 'long', - }, - { - name: 'sampling_time_space', - type: 'long', - }, - { - name: 'sampling_size', - type: 'long', - }, - { - name: 'sampling_population', - type: 'long', - }, - { - name: 'sampling_probability', - type: 'double', - }, - { - name: 'data_link_frame_size', - type: 'integer', - }, - { - name: 'ip_header_packet_section', - type: 'short', - }, - { - name: 'ip_payload_packet_section', - type: 'short', - }, - { - name: 'data_link_frame_section', - type: 'short', - }, - { - name: 'mpls_label_stack_section', - type: 'short', - }, - { - name: 'mpls_payload_packet_section', - type: 'short', - }, - { - name: 'selector_id_total_pkts_observed', - type: 'long', - }, - { - name: 'selector_id_total_pkts_selected', - type: 'long', - }, - { - name: 'absolute_error', - type: 'double', - }, - { - name: 'relative_error', - type: 'double', - }, - { - name: 'observation_time_seconds', - type: 'date', - }, - { - name: 'observation_time_milliseconds', - type: 'date', - }, - { - name: 'observation_time_microseconds', - type: 'date', - }, - { - name: 'observation_time_nanoseconds', - type: 'date', - }, - { - name: 'digest_hash_value', - type: 'long', - }, - { - name: 'hash_ip_payload_offset', - type: 'long', - }, - { - name: 'hash_ip_payload_size', - type: 'long', - }, - { - name: 'hash_output_range_min', - type: 'long', - }, - { - name: 'hash_output_range_max', - type: 'long', - }, - { - name: 'hash_selected_range_min', - type: 'long', - }, - { - name: 'hash_selected_range_max', - type: 'long', - }, - { - name: 'hash_digest_output', - type: 'boolean', - }, - { - name: 'hash_initialiser_value', - type: 'long', - }, - { - name: 'selector_name', - type: 'keyword', - }, - { - name: 'upper_ci_limit', - type: 'double', - }, - { - name: 'lower_ci_limit', - type: 'double', - }, - { - name: 'confidence_level', - type: 'double', - }, - { - name: 'information_element_data_type', - type: 'short', - }, - { - name: 'information_element_description', - type: 'keyword', - }, - { - name: 'information_element_name', - type: 'keyword', - }, - { - name: 'information_element_range_begin', - type: 'long', - }, - { - name: 'information_element_range_end', - type: 'long', - }, - { - name: 'information_element_semantics', - type: 'short', - }, - { - name: 'information_element_units', - type: 'integer', - }, - { - name: 'private_enterprise_number', - type: 'long', - }, - { - name: 'virtual_station_interface_id', - type: 'short', - }, - { - name: 'virtual_station_interface_name', - type: 'keyword', - }, - { - name: 'virtual_station_uuid', - type: 'short', - }, - { - name: 'virtual_station_name', - type: 'keyword', - }, - { - name: 'layer2_segment_id', - type: 'long', - }, - { - name: 'layer2_octet_delta_count', - type: 'long', - }, - { - name: 'layer2_octet_total_count', - type: 'long', - }, - { - name: 'ingress_unicast_packet_total_count', - type: 'long', - }, - { - name: 'ingress_multicast_packet_total_count', - type: 'long', - }, - { - name: 'ingress_broadcast_packet_total_count', - type: 'long', - }, - { - name: 'egress_unicast_packet_total_count', - type: 'long', - }, - { - name: 'egress_broadcast_packet_total_count', - type: 'long', - }, - { - name: 'monitoring_interval_start_milli_seconds', - type: 'date', - }, - { - name: 'monitoring_interval_end_milli_seconds', - type: 'date', - }, - { - name: 'port_range_start', - type: 'integer', - }, - { - name: 'port_range_end', - type: 'integer', - }, - { - name: 'port_range_step_size', - type: 'integer', - }, - { - name: 'port_range_num_ports', - type: 'integer', - }, - { - name: 'sta_mac_address', - type: 'keyword', - }, - { - name: 'sta_ipv4_address', - type: 'ip', - }, - { - name: 'wtp_mac_address', - type: 'keyword', - }, - { - name: 'ingress_interface_type', - type: 'long', - }, - { - name: 'egress_interface_type', - type: 'long', - }, - { - name: 'rtp_sequence_number', - type: 'integer', - }, - { - name: 'user_name', - type: 'keyword', - }, - { - name: 'application_category_name', - type: 'keyword', - }, - { - name: 'application_sub_category_name', - type: 'keyword', - }, - { - name: 'application_group_name', - type: 'keyword', - }, - { - name: 'original_flows_present', - type: 'long', - }, - { - name: 'original_flows_initiated', - type: 'long', - }, - { - name: 'original_flows_completed', - type: 'long', - }, - { - name: 'distinct_count_of_source_ip_address', - type: 'long', - }, - { - name: 'distinct_count_of_destination_ip_address', - type: 'long', - }, - { - name: 'distinct_count_of_source_ipv4_address', - type: 'long', - }, - { - name: 'distinct_count_of_destination_ipv4_address', - type: 'long', - }, - { - name: 'distinct_count_of_source_ipv6_address', - type: 'long', - }, - { - name: 'distinct_count_of_destination_ipv6_address', - type: 'long', - }, - { - name: 'value_distribution_method', - type: 'short', - }, - { - name: 'rfc3550_jitter_milliseconds', - type: 'long', - }, - { - name: 'rfc3550_jitter_microseconds', - type: 'long', - }, - { - name: 'rfc3550_jitter_nanoseconds', - type: 'long', - }, - { - name: 'dot1q_dei', - type: 'boolean', - }, - { - name: 'dot1q_customer_dei', - type: 'boolean', - }, - { - name: 'flow_selector_algorithm', - type: 'integer', - }, - { - name: 'flow_selected_octet_delta_count', - type: 'long', - }, - { - name: 'flow_selected_packet_delta_count', - type: 'long', - }, - { - name: 'flow_selected_flow_delta_count', - type: 'long', - }, - { - name: 'selector_id_total_flows_observed', - type: 'long', - }, - { - name: 'selector_id_total_flows_selected', - type: 'long', - }, - { - name: 'sampling_flow_interval', - type: 'long', - }, - { - name: 'sampling_flow_spacing', - type: 'long', - }, - { - name: 'flow_sampling_time_interval', - type: 'long', - }, - { - name: 'flow_sampling_time_spacing', - type: 'long', - }, - { - name: 'hash_flow_domain', - type: 'integer', - }, - { - name: 'transport_octet_delta_count', - type: 'long', - }, - { - name: 'transport_packet_delta_count', - type: 'long', - }, - { - name: 'original_exporter_ipv4_address', - type: 'ip', - }, - { - name: 'original_exporter_ipv6_address', - type: 'ip', - }, - { - name: 'original_observation_domain_id', - type: 'long', - }, - { - name: 'intermediate_process_id', - type: 'long', - }, - { - name: 'ignored_data_record_total_count', - type: 'long', - }, - { - name: 'data_link_frame_type', - type: 'integer', - }, - { - name: 'section_offset', - type: 'integer', - }, - { - name: 'section_exported_octets', - type: 'integer', - }, - { - name: 'dot1q_service_instance_tag', - type: 'short', - }, - { - name: 'dot1q_service_instance_id', - type: 'long', - }, - { - name: 'dot1q_service_instance_priority', - type: 'short', - }, - { - name: 'dot1q_customer_source_mac_address', - type: 'keyword', - }, - { - name: 'dot1q_customer_destination_mac_address', - type: 'keyword', - }, - { - name: 'post_layer2_octet_delta_count', - type: 'long', - }, - { - name: 'post_mcast_layer2_octet_delta_count', - type: 'long', - }, - { - name: 'post_layer2_octet_total_count', - type: 'long', - }, - { - name: 'post_mcast_layer2_octet_total_count', - type: 'long', - }, - { - name: 'minimum_layer2_total_length', - type: 'long', - }, - { - name: 'maximum_layer2_total_length', - type: 'long', - }, - { - name: 'dropped_layer2_octet_delta_count', - type: 'long', - }, - { - name: 'dropped_layer2_octet_total_count', - type: 'long', - }, - { - name: 'ignored_layer2_octet_total_count', - type: 'long', - }, - { - name: 'not_sent_layer2_octet_total_count', - type: 'long', - }, - { - name: 'layer2_octet_delta_sum_of_squares', - type: 'long', - }, - { - name: 'layer2_octet_total_sum_of_squares', - type: 'long', - }, - { - name: 'layer2_frame_delta_count', - type: 'long', - }, - { - name: 'layer2_frame_total_count', - type: 'long', - }, - { - name: 'pseudo_wire_destination_ipv4_address', - type: 'ip', - }, - { - name: 'ignored_layer2_frame_total_count', - type: 'long', - }, - { - name: 'mib_object_value_integer', - type: 'integer', - }, - { - name: 'mib_object_value_octet_string', - type: 'short', - }, - { - name: 'mib_object_value_oid', - type: 'short', - }, - { - name: 'mib_object_value_bits', - type: 'short', - }, - { - name: 'mib_object_value_ip_address', - type: 'ip', - }, - { - name: 'mib_object_value_counter', - type: 'long', - }, - { - name: 'mib_object_value_gauge', - type: 'long', - }, - { - name: 'mib_object_value_time_ticks', - type: 'long', - }, - { - name: 'mib_object_value_unsigned', - type: 'long', - }, - { - name: 'mib_object_identifier', - type: 'short', - }, - { - name: 'mib_sub_identifier', - type: 'long', - }, - { - name: 'mib_index_indicator', - type: 'long', - }, - { - name: 'mib_capture_time_semantics', - type: 'short', - }, - { - name: 'mib_context_engine_id', - type: 'short', - }, - { - name: 'mib_context_name', - type: 'keyword', - }, - { - name: 'mib_object_name', - type: 'keyword', - }, - { - name: 'mib_object_description', - type: 'keyword', - }, - { - name: 'mib_object_syntax', - type: 'keyword', - }, - { - name: 'mib_module_name', - type: 'keyword', - }, - { - name: 'mobile_imsi', - type: 'keyword', - }, - { - name: 'mobile_msisdn', - type: 'keyword', - }, - { - name: 'http_status_code', - type: 'integer', - }, - { - name: 'source_transport_ports_limit', - type: 'integer', - }, - { - name: 'http_request_method', - type: 'keyword', - }, - { - name: 'http_request_host', - type: 'keyword', - }, - { - name: 'http_request_target', - type: 'keyword', - }, - { - name: 'http_message_version', - type: 'keyword', - }, - { - name: 'nat_instance_id', - type: 'long', - }, - { - name: 'internal_address_realm', - type: 'short', - }, - { - name: 'external_address_realm', - type: 'short', - }, - { - name: 'nat_quota_exceeded_event', - type: 'long', - }, - { - name: 'nat_threshold_event', - type: 'long', - }, - { - name: 'http_user_agent', - type: 'keyword', - }, - { - name: 'http_content_type', - type: 'keyword', - }, - { - name: 'http_reason_phrase', - type: 'keyword', - }, - { - name: 'max_session_entries', - type: 'long', - }, - { - name: 'max_bib_entries', - type: 'long', - }, - { - name: 'max_entries_per_user', - type: 'long', - }, - { - name: 'max_subscribers', - type: 'long', - }, - { - name: 'max_fragments_pending_reassembly', - type: 'long', - }, - { - name: 'address_pool_high_threshold', - type: 'long', - }, - { - name: 'address_pool_low_threshold', - type: 'long', - }, - { - name: 'address_port_mapping_high_threshold', - type: 'long', - }, - { - name: 'address_port_mapping_low_threshold', - type: 'long', - }, - { - name: 'address_port_mapping_per_user_high_threshold', - type: 'long', - }, - { - name: 'global_address_mapping_high_threshold', - type: 'long', - }, - { - name: 'vpn_identifier', - type: 'short', - }, - ], - }, - ], - }, - { - key: 's3', - title: 's3', - description: 'S3 fields from s3 input.\n', - release: 'beta', - fields: [ - { - name: 'bucket_name', - type: 'keyword', - description: 'Name of the S3 bucket that this log retrieved from.\n', - }, - { - name: 'object_key', - type: 'keyword', - description: 'Name of the S3 object that this log retrieved from.\n', - }, - ], - }, - { - key: 'cef', - title: 'Decode CEF processor fields', - description: 'Common Event Format (CEF) data.\n', - fields: [ - { - name: 'cef', - type: 'group', - description: - 'By default the `decode_cef` processor writes all data from the CEF message to this `cef` object. It contains the CEF header fields and the extension data.\n', - fields: [ - { - name: 'version', - type: 'keyword', - description: 'Version of the CEF specification used by the message.\n', - }, - { - name: 'device.vendor', - type: 'keyword', - description: 'Vendor of the device that produced the message.\n', - }, - { - name: 'device.product', - type: 'keyword', - description: 'Product of the device that produced the message.\n', - }, - { - name: 'device.version', - type: 'keyword', - description: 'Version of the product that produced the message.\n', - }, - { - name: 'device.event_class_id', - type: 'keyword', - description: 'Unique identifier of the event type.\n', - }, - { - name: 'severity', - type: 'keyword', - example: 'Very-High', - description: - 'Importance of the event. The valid string values are Unknown, Low, Medium, High, and Very-High. The valid integer values are 0-3=Low, 4-6=Medium, 7- 8=High, and 9-10=Very-High.\n', - }, - { - name: 'name', - type: 'keyword', - description: 'Short description of the event.\n', - }, - { - name: 'extensions', - type: 'group', - description: 'Collection of key-value pairs carried in the CEF extension field.\n', - default_field: false, - fields: [ - { - name: 'agentAddress', - type: 'ip', - description: 'The IP address of the ArcSight connector that processed the event.', - }, - { - name: 'agentDnsDomain', - type: 'keyword', - description: - 'The DNS domain name of the ArcSight connector that processed the event.', - }, - { - name: 'agentHostName', - type: 'keyword', - description: 'The hostname of the ArcSight connector that processed the event.', - }, - { - name: 'agentId', - type: 'keyword', - description: 'The agent ID of the ArcSight connector that processed the event.', - }, - { - name: 'agentMacAddress', - type: 'keyword', - description: 'The MAC address of the ArcSight connector that processed the event.', - }, - { - name: 'agentNtDomain', - type: 'keyword', - description: '', - }, - { - name: 'agentReceiptTime', - type: 'date', - description: - 'The time at which information about the event was received by the ArcSight connector.', - }, - { - name: 'agentTimeZone', - type: 'keyword', - description: - 'The agent time zone of the ArcSight connector that processed the event.', - }, - { - name: 'agentTranslatedAddress', - type: 'ip', - description: '', - }, - { - name: 'agentTranslatedZoneExternalID', - type: 'keyword', - description: '', - }, - { - name: 'agentTranslatedZoneURI', - type: 'keyword', - description: '', - }, - { - name: 'agentType', - type: 'keyword', - description: 'The agent type of the ArcSight connector that processed the event', - }, - { - name: 'agentVersion', - type: 'keyword', - description: 'The version of the ArcSight connector that processed the event.', - }, - { - name: 'agentZoneExternalID', - type: 'keyword', - description: '', - }, - { - name: 'agentZoneURI', - type: 'keyword', - description: '', - }, - { - name: 'applicationProtocol', - type: 'keyword', - description: - 'Application level protocol, example values are HTTP, HTTPS, SSHv2, Telnet, POP, IMPA, IMAPS, and so on.', - }, - { - name: 'baseEventCount', - type: 'long', - description: - 'A count associated with this event. How many times was this same event observed? Count can be omitted if it is 1.', - }, - { - name: 'bytesIn', - type: 'long', - description: - 'Number of bytes transferred inbound, relative to the source to destination relationship, meaning that data was flowing from source to destination.', - }, - { - name: 'bytesOut', - type: 'long', - description: - 'Number of bytes transferred outbound relative to the source to destination relationship. For example, the byte number of data flowing from the destination to the source.', - }, - { - name: 'customerExternalID', - type: 'keyword', - description: '', - }, - { - name: 'customerURI', - type: 'keyword', - description: '', - }, - { - name: 'destinationAddress', - type: 'ip', - description: - 'Identifies the destination address that the event refers to in an IP network. The format is an IPv4 address.', - }, - { - name: 'destinationDnsDomain', - type: 'keyword', - description: - 'The DNS domain part of the complete fully qualified domain name (FQDN).', - }, - { - name: 'destinationGeoLatitude', - type: 'double', - description: - "The latitudinal value from which the destination's IP address belongs.", - }, - { - name: 'destinationGeoLongitude', - type: 'double', - description: - "The longitudinal value from which the destination's IP address belongs.", - }, - { - name: 'destinationHostName', - type: 'keyword', - description: - 'Identifies the destination that an event refers to in an IP network. The format should be a fully qualified domain name (FQDN) associated with the destination node, when a node is available.', - }, - { - name: 'destinationMacAddress', - type: 'keyword', - description: 'Six colon-seperated hexadecimal numbers.', - }, - { - name: 'destinationNtDomain', - type: 'keyword', - description: 'The Windows domain name of the destination address.', - }, - { - name: 'destinationPort', - type: 'long', - description: 'The valid port numbers are between 0 and 65535.', - }, - { - name: 'destinationProcessId', - type: 'long', - description: - 'Provides the ID of the destination process associated with the event. For example, if an event contains process ID 105, "105" is the process ID.', - }, - { - name: 'destinationProcessName', - type: 'keyword', - description: "The name of the event's destination process.", - }, - { - name: 'destinationServiceName', - type: 'keyword', - description: 'The service targeted by this event.', - }, - { - name: 'destinationTranslatedAddress', - type: 'ip', - description: - 'Identifies the translated destination that the event refers to in an IP network.', - }, - { - name: 'destinationTranslatedPort', - type: 'long', - description: - 'Port after it was translated; for example, a firewall. Valid port numbers are 0 to 65535.', - }, - { - name: 'destinationTranslatedZoneExternalID', - type: 'keyword', - description: '', - }, - { - name: 'destinationTranslatedZoneURI', - type: 'keyword', - description: - 'The URI for the Translated Zone that the destination asset has been assigned to in ArcSight.', - }, - { - name: 'destinationUserId', - type: 'keyword', - description: - 'Identifies the destination user by ID. For example, in UNIX, the root user is generally associated with user ID 0.', - }, - { - name: 'destinationUserName', - type: 'keyword', - description: - "Identifies the destination user by name. This is the user associated with the event's destination. Email addresses are often mapped into the UserName fields. The recipient is a candidate to put into this field.", - }, - { - name: 'destinationUserPrivileges', - type: 'keyword', - description: - 'The typical values are "Administrator", "User", and "Guest". This identifies the destination user\'s privileges. In UNIX, for example, activity executed on the root user would be identified with destinationUser Privileges of "Administrator".', - }, - { - name: 'destinationZoneExternalID', - type: 'keyword', - description: '', - }, - { - name: 'destinationZoneURI', - type: 'keyword', - description: - 'The URI for the Zone that the destination asset has been assigned to in ArcSight.', - }, - { - name: 'deviceAction', - type: 'keyword', - description: 'Action taken by the device.', - }, - { - name: 'deviceAddress', - type: 'ip', - description: - 'Identifies the device address that an event refers to in an IP network.', - }, - { - name: 'deviceCustomFloatingPoint1Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomFloatingPoint3Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomFloatingPoint4Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomDate1', - type: 'date', - description: - 'One of two timestamp fields available to map fields that do not apply to any other in this dictionary.', - }, - { - name: 'deviceCustomDate1Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomDate2', - type: 'date', - description: - 'One of two timestamp fields available to map fields that do not apply to any other in this dictionary.', - }, - { - name: 'deviceCustomDate2Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomFloatingPoint1', - type: 'double', - description: - 'One of four floating point fields available to map fields that do not apply to any other in this dictionary.', - }, - { - name: 'deviceCustomFloatingPoint2', - type: 'double', - description: - 'One of four floating point fields available to map fields that do not apply to any other in this dictionary.', - }, - { - name: 'deviceCustomFloatingPoint2Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomFloatingPoint3', - type: 'double', - description: - 'One of four floating point fields available to map fields that do not apply to any other in this dictionary.', - }, - { - name: 'deviceCustomFloatingPoint4', - type: 'double', - description: - 'One of four floating point fields available to map fields that do not apply to any other in this dictionary.', - }, - { - name: 'deviceCustomIPv6Address1', - type: 'ip', - description: - 'One of four IPv6 address fields available to map fields that do not apply to any other in this dictionary.', - }, - { - name: 'deviceCustomIPv6Address1Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomIPv6Address2', - type: 'ip', - description: - 'One of four IPv6 address fields available to map fields that do not apply to any other in this dictionary.', - }, - { - name: 'deviceCustomIPv6Address2Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomIPv6Address3', - type: 'ip', - description: - 'One of four IPv6 address fields available to map fields that do not apply to any other in this dictionary.', - }, - { - name: 'deviceCustomIPv6Address3Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomIPv6Address4', - type: 'ip', - description: - 'One of four IPv6 address fields available to map fields that do not apply to any other in this dictionary.', - }, - { - name: 'deviceCustomIPv6Address4Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomNumber1', - type: 'long', - description: - 'One of three number fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', - }, - { - name: 'deviceCustomNumber1Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomNumber2', - type: 'long', - description: - 'One of three number fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', - }, - { - name: 'deviceCustomNumber2Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomNumber3', - type: 'long', - description: - 'One of three number fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', - }, - { - name: 'deviceCustomNumber3Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomString1', - type: 'keyword', - description: - 'One of six strings available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', - }, - { - name: 'deviceCustomString1Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomString2', - type: 'keyword', - description: - 'One of six strings available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', - }, - { - name: 'deviceCustomString2Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomString3', - type: 'keyword', - description: - 'One of six strings available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', - }, - { - name: 'deviceCustomString3Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomString4', - type: 'keyword', - description: - 'One of six strings available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', - }, - { - name: 'deviceCustomString4Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomString5', - type: 'keyword', - description: - 'One of six strings available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', - }, - { - name: 'deviceCustomString5Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceCustomString6', - type: 'keyword', - description: - 'One of six strings available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', - }, - { - name: 'deviceCustomString6Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceDirection', - type: 'long', - description: - 'Any information about what direction the observed communication has taken. The following values are supported - "0" for inbound or "1" for outbound.', - }, - { - name: 'deviceDnsDomain', - type: 'keyword', - description: - 'The DNS domain part of the complete fully qualified domain name (FQDN).', - }, - { - name: 'deviceEventCategory', - type: 'keyword', - description: - 'Represents the category assigned by the originating device. Devices often use their own categorization schema to classify event. Example "/Monitor/Disk/Read".', - }, - { - name: 'deviceExternalId', - type: 'keyword', - description: 'A name that uniquely identifies the device generating this event.', - }, - { - name: 'deviceFacility', - type: 'keyword', - description: - 'The facility generating this event. For example, Syslog has an explicit facility associated with every event.', - }, - { - name: 'deviceFlexNumber1', - type: 'long', - description: - 'One of two alternative number fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', - }, - { - name: 'deviceFlexNumber1Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceFlexNumber2', - type: 'long', - description: - 'One of two alternative number fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', - }, - { - name: 'deviceFlexNumber2Label', - type: 'keyword', - description: - 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', - }, - { - name: 'deviceHostName', - type: 'keyword', - description: - 'The format should be a fully qualified domain name (FQDN) associated with the device node, when a node is available.', - }, - { - name: 'deviceInboundInterface', - type: 'keyword', - description: 'Interface on which the packet or data entered the device.', - }, - { - name: 'deviceMacAddress', - type: 'keyword', - description: 'Six colon-separated hexadecimal numbers.', - }, - { - name: 'deviceNtDomain', - type: 'keyword', - description: 'The Windows domain name of the device address.', - }, - { - name: 'deviceOutboundInterface', - type: 'keyword', - description: 'Interface on which the packet or data left the device.', - }, - { - name: 'devicePayloadId', - type: 'keyword', - description: 'Unique identifier for the payload associated with the event.', - }, - { - name: 'deviceProcessId', - type: 'long', - description: 'Provides the ID of the process on the device generating the event.', - }, - { - name: 'deviceProcessName', - type: 'keyword', - description: - 'Process name associated with the event. An example might be the process generating the syslog entry in UNIX.', - }, - { - name: 'deviceReceiptTime', - type: 'date', - description: - 'The time at which the event related to the activity was received. The format is MMM dd yyyy HH:mm:ss or milliseconds since epoch (Jan 1st 1970)', - }, - { - name: 'deviceTimeZone', - type: 'keyword', - description: 'The timezone for the device generating the event.', - }, - { - name: 'deviceTranslatedAddress', - type: 'ip', - description: - 'Identifies the translated device address that the event refers to in an IP network.', - }, - { - name: 'deviceTranslatedZoneExternalID', - type: 'keyword', - description: '', - }, - { - name: 'deviceTranslatedZoneURI', - type: 'keyword', - description: - 'The URI for the Translated Zone that the device asset has been assigned to in ArcSight.', - }, - { - name: 'deviceZoneExternalID', - type: 'keyword', - description: '', - }, - { - name: 'deviceZoneURI', - type: 'keyword', - description: - 'Thee URI for the Zone that the device asset has been assigned to in ArcSight.', - }, - { - name: 'endTime', - type: 'date', - description: - 'The time at which the activity related to the event ended. The format is MMM dd yyyy HH:mm:ss or milliseconds since epoch (Jan 1st1970). An example would be reporting the end of a session.', - }, - { - name: 'eventId', - type: 'long', - description: 'This is a unique ID that ArcSight assigns to each event.', - }, - { - name: 'eventOutcome', - type: 'keyword', - description: "Displays the outcome, usually as 'success' or 'failure'.", - }, - { - name: 'externalId', - type: 'keyword', - description: - 'The ID used by an originating device. They are usually increasing numbers, associated with events.', - }, - { - name: 'fileCreateTime', - type: 'date', - description: 'Time when the file was created.', - }, - { - name: 'fileHash', - type: 'keyword', - description: 'Hash of a file.', - }, - { - name: 'fileId', - type: 'keyword', - description: 'An ID associated with a file could be the inode.', - }, - { - name: 'fileModificationTime', - type: 'date', - description: 'Time when the file was last modified.', - }, - { - name: 'filename', - type: 'keyword', - description: 'Name of the file only (without its path).', - }, - { - name: 'filePath', - type: 'keyword', - description: 'Full path to the file, including file name itself.', - }, - { - name: 'filePermission', - type: 'keyword', - description: 'Permissions of the file.', - }, - { - name: 'fileSize', - type: 'long', - description: 'Size of the file.', - }, - { - name: 'fileType', - type: 'keyword', - description: 'Type of file (pipe, socket, etc.)', - }, - { - name: 'flexDate1', - type: 'date', - description: - 'A timestamp field available to map a timestamp that does not apply to any other defined timestamp field in this dictionary. Use all flex fields sparingly and seek a more specific, dictionary supplied field when possible. These fields are typically reserved for customer use and should not be set by vendors unless necessary.', - }, - { - name: 'flexDate1Label', - type: 'keyword', - description: - 'The label field is a string and describes the purpose of the flex field.', - }, - { - name: 'flexString1', - type: 'keyword', - description: - 'One of four floating point fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible. These fields are typically reserved for customer use and should not be set by vendors unless necessary.', - }, - { - name: 'flexString2', - type: 'keyword', - description: - 'One of four floating point fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible. These fields are typically reserved for customer use and should not be set by vendors unless necessary.', - }, - { - name: 'flexString1Label', - type: 'keyword', - description: - 'The label field is a string and describes the purpose of the flex field.', - }, - { - name: 'flexString2Label', - type: 'keyword', - description: - 'The label field is a string and describes the purpose of the flex field.', - }, - { - name: 'message', - type: 'keyword', - description: - 'An arbitrary message giving more details about the event. Multi-line entries can be produced by using \\n as the new line separator.', - }, - { - name: 'oldFileCreateTime', - type: 'date', - description: 'Time when old file was created.', - }, - { - name: 'oldFileHash', - type: 'keyword', - description: 'Hash of the old file.', - }, - { - name: 'oldFileId', - type: 'keyword', - description: 'An ID associated with the old file could be the inode.', - }, - { - name: 'oldFileModificationTime', - type: 'date', - description: 'Time when old file was last modified.', - }, - { - name: 'oldFileName', - type: 'keyword', - description: 'Name of the old file.', - }, - { - name: 'oldFilePath', - type: 'keyword', - description: 'Full path to the old file, including the file name itself.', - }, - { - name: 'oldFilePermission', - type: 'keyword', - description: 'Permissions of the old file.', - }, - { - name: 'oldFileSize', - type: 'long', - description: 'Size of the old file.', - }, - { - name: 'oldFileType', - type: 'keyword', - description: 'Type of the old file (pipe, socket, etc.)', - }, - { - name: 'rawEvent', - type: 'keyword', - description: '', - }, - { - name: 'Reason', - type: 'keyword', - description: - 'The reason an audit event was generated. For example "bad password" or "unknown user". This could also be an error or return code. Example "0x1234".', - }, - { - name: 'requestClientApplication', - type: 'keyword', - description: 'The User-Agent associated with the request.', - }, - { - name: 'requestContext', - type: 'keyword', - description: - 'Description of the content from which the request originated (for example, HTTP Referrer)', - }, - { - name: 'requestCookies', - type: 'keyword', - description: 'Cookies associated with the request.', - }, - { - name: 'requestMethod', - type: 'keyword', - description: 'The HTTP method used to access a URL.', - }, - { - name: 'requestUrl', - type: 'keyword', - description: - 'In the case of an HTTP request, this field contains the URL accessed. The URL should contain the protocol as well.', - }, - { - name: 'sourceAddress', - type: 'ip', - description: 'Identifies the source that an event refers to in an IP network.', - }, - { - name: 'sourceDnsDomain', - type: 'keyword', - description: - 'The DNS domain part of the complete fully qualified domain name (FQDN).', - }, - { - name: 'sourceGeoLatitude', - type: 'double', - description: '', - }, - { - name: 'sourceGeoLongitude', - type: 'double', - description: '', - }, - { - name: 'sourceHostName', - type: 'keyword', - description: - "Identifies the source that an event refers to in an IP network. The format should be a fully qualified domain name (FQDN) associated with the source node, when a mode is available. Examples: 'host' or 'host.domain.com'.\n", - }, - { - name: 'sourceMacAddress', - type: 'keyword', - example: '00:0d:60:af:1b:61', - description: 'Six colon-separated hexadecimal numbers.', - }, - { - name: 'sourceNtDomain', - type: 'keyword', - description: 'The Windows domain name for the source address.', - }, - { - name: 'sourcePort', - type: 'long', - description: 'The valid port numbers are 0 to 65535.', - }, - { - name: 'sourceProcessId', - type: 'long', - description: 'The ID of the source process associated with the event.', - }, - { - name: 'sourceProcessName', - type: 'keyword', - description: "The name of the event's source process.", - }, - { - name: 'sourceServiceName', - type: 'keyword', - description: 'The service that is responsible for generating this event.', - }, - { - name: 'sourceTranslatedAddress', - type: 'ip', - description: - 'Identifies the translated source that the event refers to in an IP network.', - }, - { - name: 'sourceTranslatedPort', - type: 'long', - description: - 'A port number after being translated by, for example, a firewall. Valid port numbers are 0 to 65535.', - }, - { - name: 'sourceTranslatedZoneExternalID', - type: 'keyword', - description: '', - }, - { - name: 'sourceTranslatedZoneURI', - type: 'keyword', - description: - 'The URI for the Translated Zone that the destination asset has been assigned to in ArcSight.', - }, - { - name: 'sourceUserId', - type: 'keyword', - description: - 'Identifies the source user by ID. This is the user associated with the source of the event. For example, in UNIX, the root user is generally associated with user ID 0.', - }, - { - name: 'sourceUserName', - type: 'keyword', - description: - 'Identifies the source user by name. Email addresses are also mapped into the UserName fields. The sender is a candidate to put into this field.', - }, - { - name: 'sourceUserPrivileges', - type: 'keyword', - description: - 'The typical values are "Administrator", "User", and "Guest". It identifies the source user\'s privileges. In UNIX, for example, activity executed by the root user would be identified with "Administrator".', - }, - { - name: 'sourceZoneExternalID', - type: 'keyword', - description: '', - }, - { - name: 'sourceZoneURI', - type: 'keyword', - description: - 'The URI for the Zone that the source asset has been assigned to in ArcSight.', - }, - { - name: 'startTime', - type: 'date', - description: - 'The time when the activity the event referred to started. The format is MMM dd yyyy HH:mm:ss or milliseconds since epoch (Jan 1st 1970)', - }, - { - name: 'transportProtocol', - type: 'keyword', - description: - 'Identifies the Layer-4 protocol used. The possible values are protocols such as TCP or UDP.', - }, - { - name: 'type', - type: 'long', - description: - '0 means base event, 1 means aggregated, 2 means correlation, and 3 means action. This field can be omitted for base events (type 0).', - }, - { - name: 'categoryDeviceType', - type: 'keyword', - description: 'Device type. Examples - Proxy, IDS, Web Server', - }, - { - name: 'categoryObject', - type: 'keyword', - description: - 'Object that the event is about. For example it can be an operating sytem, database, file, etc.', - }, - { - name: 'categoryBehavior', - type: 'keyword', - description: - "Action or a behavior associated with an event. It's what is being done to the object.", - }, - { - name: 'categoryTechnique', - type: 'keyword', - description: 'Technique being used (e.g. /DoS).', - }, - { - name: 'categoryDeviceGroup', - type: 'keyword', - description: 'General device group like Firewall.', - }, - { - name: 'categorySignificance', - type: 'keyword', - description: 'Characterization of the importance of the event.', - }, - { - name: 'categoryOutcome', - type: 'keyword', - description: 'Outcome of the event (e.g. sucess, failure, or attempt).', - }, - { - name: 'managerReceiptTime', - type: 'date', - description: 'When the Arcsight ESM received the event.', - }, - ], - }, - ], - }, - { - name: 'source.service.name', - type: 'keyword', - description: 'Service that is the source of the event.', - }, - { - name: 'destination.service.name', - type: 'keyword', - description: 'Service that is the target of the event.', - }, - ], - }, -]; diff --git a/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/index.ts b/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/index.ts deleted file mode 100644 index bd7e7d4eec83b5..00000000000000 --- a/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { auditbeatSchema } from './auditbeat'; -export { filebeatSchema } from './filebeat'; -export { packetbeatSchema } from './packetbeat'; -export { winlogbeatSchema } from './winlogbeat'; -export { ecsSchema } from './ecs'; - -export const extraSchemaField = { - _id: { - description: 'Each document has an _id that uniquely identifies it', - example: 'Y-6TfmcB0WOhS6qyMv3s', - footnote: '', - group: 1, - level: 'core', - name: '_id', - required: true, - type: 'keyword', - }, - _index: { - description: - 'An index is like a ‘database’ in a relational database. It has a mapping which defines multiple types. An index is a logical namespace which maps to one or more primary shards and can have zero or more replica shards.', - example: 'auditbeat-8.0.0-2019.02.19-000001', - footnote: '', - group: 1, - level: 'core', - name: '_index', - required: true, - type: 'keyword', - }, -}; - -export const baseCategoryFields = ['@timestamp', 'labels', 'message', 'tags']; diff --git a/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/packetbeat.ts b/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/packetbeat.ts deleted file mode 100644 index 0be2e48fe46689..00000000000000 --- a/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/packetbeat.ts +++ /dev/null @@ -1,8556 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/** - * An instance of the unmodified schema exported from auditbeat-8.0.0-SNAPSHOT-darwin-x86_64.tar.gz - * - */ - -import { Schema } from '../type'; - -export const packetbeatSchema: Schema = [ - { - key: 'ecs', - title: 'ECS', - description: 'ECS Fields.', - fields: [ - { - name: '@timestamp', - level: 'core', - required: true, - type: 'date', - description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', - example: '2016-05-23T08:05:34.853Z', - }, - { - name: 'labels', - level: 'core', - type: 'object', - object_type: 'keyword', - description: - 'Custom key/value pairs.\n\nCan be used to add meta information to events. Should not contain nested objects.\nAll values are stored as keyword.\n\nExample: `docker` and `k8s` labels.', - example: '{"application": "foo-bar", "env": "production"}', - }, - { - name: 'message', - level: 'core', - type: 'text', - description: - 'For log events the message field contains the log message, optimized\nfor viewing in a log viewer.\n\nFor structured logs without an original message field, other fields can be concatenated\nto form a human-readable summary of the event.\n\nIf multiple messages exist, they can be combined into one message.', - example: 'Hello World', - }, - { - name: 'tags', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - }, - { - name: 'agent', - title: 'Agent', - group: 2, - description: - 'The agent fields contain the data about the software entity, if\nany, that collects, detects, or observes events on a host, or takes measurements\non a host.\n\nExamples include Beats. Agents may also run on observers. ECS agent.* fields\nshall be populated with details of the agent running on the host or observer\nwhere the event happened or the measurement was taken.', - footnote: - 'Examples: In the case of Beats for logs, the agent.name is filebeat.\nFor APM, it is the agent running in the app/service. The agent information does\nnot change if data is sent through queuing systems like Kafka, Redis, or processing\nsystems such as Logstash or APM Server.', - type: 'group', - fields: [ - { - name: 'ephemeral_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', - example: '8a4f500d', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', - example: 'foo', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', - example: 'filebeat', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Version of the agent.', - example: '6.0.0-rc2', - }, - ], - }, - { - name: 'as', - title: 'Autonomous System', - group: 2, - description: - 'An autonomous system (AS) is a collection of connected Internet Protocol\n(IP) routing prefixes under the control of one or more network operators on\nbehalf of a single administrative entity or domain that presents a common, clearly\ndefined routing policy to the internet.', - type: 'group', - fields: [ - { - name: 'number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - ], - }, - { - name: 'client', - title: 'Client', - group: 2, - description: - 'A client is defined as the initiator of a network connection for\nevents regarding sessions, connections, or bidirectional flow records.\n\nFor TCP events, the client is the initiator of the TCP connection that sends\nthe SYN packet(s). For other protocols, the client is generally the initiator\nor requestor in the network transaction. Some systems use the term "originator"\nto refer the client in TCP connections. The client fields describe details about\nthe system acting as the client in the network event. Client fields are usually\npopulated in conjunction with server fields. Client fields are generally not\npopulated for packet-level events.\n\nClient / server representations can add semantic context to an exchange, which\nis helpful to visualize the data in certain situations. If your context falls\nin that category, you should still ensure that source and destination are filled\nappropriately.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event client addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the client to the server.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Client domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the client.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the client.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated IP of source based NAT sessions (e.g. internal client\nto internet).\n\nTypically connections traversing load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Translated port of source based NAT sessions (e.g. internal client\nto internet).\n\nTypically connections traversing load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the client to the server.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the client.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered client domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'cloud', - title: 'Cloud', - group: 2, - description: 'Fields related to the cloud or infrastructure the events are coming\nfrom.', - footnote: - 'Examples: If Metricbeat is running on an EC2 host and fetches data\nfrom its host, the cloud info contains the data about this machine. If Metricbeat\nruns on a remote machine outside the cloud and fetches data from a service running\nin the cloud, the field contains cloud data from the machine the service is\nrunning on.', - type: 'group', - fields: [ - { - name: 'account.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The cloud account or organization id used to identify different\nentities in a multi-tenant environment.\n\nExamples: AWS account id, Google Cloud ORG Id, or other unique identifier.', - example: 666777888999, - }, - { - name: 'availability_zone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Availability zone in which this host is running.', - example: 'us-east-1c', - }, - { - name: 'instance.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Instance ID of the host machine.', - example: 'i-1234567890abcdef0', - }, - { - name: 'instance.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Instance name of the host machine.', - }, - { - name: 'machine.type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Machine type of the host machine.', - example: 't2.medium', - }, - { - name: 'provider', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the cloud provider. Example values are aws, azure, gcp,\nor digitalocean.', - example: 'aws', - }, - { - name: 'region', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Region in which this host is running.', - example: 'us-east-1', - }, - ], - }, - { - name: 'code_signature', - title: 'Code Signature', - group: 2, - description: 'These fields contain information about binary code signatures.', - type: 'group', - fields: [ - { - name: 'exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - ], - }, - { - name: 'container', - title: 'Container', - group: 2, - description: - 'Container fields are used for meta information about the specific\ncontainer that is the source of information.\n\nThese fields help correlate data based containers from any runtime.', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique container id.', - }, - { - name: 'image.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the image the container was built on.', - }, - { - name: 'image.tag', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Container image tags.', - }, - { - name: 'labels', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: 'Image labels.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Container name.', - }, - { - name: 'runtime', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Runtime managing this container.', - example: 'docker', - }, - ], - }, - { - name: 'destination', - title: 'Destination', - group: 2, - description: - 'Destination fields describe details about the destination of a packet/event.\n\nDestination fields are usually populated in conjunction with source fields.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event destination addresses are defined ambiguously. The\nevent will sometimes list an IP, a domain or a unix socket. You should always\nstore the raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the destination to the source.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Destination domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the destination.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the destination.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated ip of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Port the source session is translated to by NAT Device.\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the destination to the source.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the destination.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered destination domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'dll', - title: 'DLL', - group: 2, - description: - 'These fields contain information about code libraries dynamically\nloaded into processes.\n\n\nMany operating systems refer to "shared code libraries" with different names,\nbut this field set refers to all of the following:\n\n* Dynamic-link library (`.dll`) commonly used on Windows\n\n* Shared Object (`.so`) commonly used on Unix-like operating systems\n\n* Dynamic library (`.dylib`) commonly used on macOS', - type: 'group', - fields: [ - { - name: 'code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - default_field: false, - }, - { - name: 'hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - default_field: false, - }, - { - name: 'hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - default_field: false, - }, - { - name: 'hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - default_field: false, - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the library.\n\nThis generally maps to the name of the file on disk.', - example: 'kernel32.dll', - default_field: false, - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Full file path of the library.', - example: 'C:\\Windows\\System32\\kernel32.dll', - default_field: false, - }, - { - name: 'pe.company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'pe.description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'pe.file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'pe.original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'pe.product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - ], - }, - { - name: 'dns', - title: 'DNS', - group: 2, - description: - 'Fields describing DNS queries and answers.\n\nDNS events should either represent a single DNS query prior to getting answers\n(`dns.type:query`) or they should represent a full exchange and contain the\nquery details as well as all of the answers that were provided for this query\n(`dns.type:answer`).', - type: 'group', - fields: [ - { - name: 'answers', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'An array containing an object for each answer section returned\nby the server.\n\nThe main keys that should be present in these objects are defined by ECS.\nRecords that have more information may contain more keys than what ECS defines.\n\nNot all DNS data sources give all details about DNS answers. At minimum, answer\nobjects must contain the `data` key. If more information is available, map\nas much of it to ECS as possible, and add any additional fields to the answer\nobjects as custom fields.', - }, - { - name: 'answers.class', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The class of DNS data contained in this resource record.', - example: 'IN', - }, - { - name: 'answers.data', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The data describing the resource.\n\nThe meaning of this data depends on the type and class of the resource record.', - example: '10.10.10.10', - }, - { - name: 'answers.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The domain name to which this resource record pertains.\n\nIf a chain of CNAME is being resolved, each answer `name` should be the\none that corresponds with the answer `data`. It should not simply be the\noriginal `question.name` repeated.', - example: 'www.google.com', - }, - { - name: 'answers.ttl', - level: 'extended', - type: 'long', - description: - 'The time interval in seconds that this resource record may be cached\nbefore it should be discarded. Zero values mean that the data should not be\ncached.', - example: 180, - }, - { - name: 'answers.type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The type of data contained in this resource record.', - example: 'CNAME', - }, - { - name: 'header_flags', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of 2 letter DNS header flags.\n\nExpected values are: AA, TC, RD, RA, AD, CD, DO.', - example: ['RD', 'RA'], - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The DNS packet identifier assigned by the program that generated\nthe query. The identifier is copied to the response.', - example: 62111, - }, - { - name: 'op_code', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The DNS operation code that specifies the kind of query in the\nmessage. This value is set by the originator of a query and copied into the\nresponse.', - example: 'QUERY', - }, - { - name: 'question.class', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The class of records being queried.', - example: 'IN', - }, - { - name: 'question.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The name being queried.\n\nIf the name field contains non-printable characters (below 32 or above 126),\nthose characters should be represented as escaped base 10 integers (\\DDD).\nBack slashes and quotes should be escaped. Tabs, carriage returns, and line\nfeeds should be converted to \\t, \\r, and \\n respectively.', - example: 'www.google.com', - }, - { - name: 'question.registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'question.subdomain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The subdomain is all of the labels under the registered_domain.\n\nIf the domain has multiple levels of subdomain, such as "sub2.sub1.example.com",\nthe subdomain field should contain "sub2.sub1", with no trailing period.', - example: 'www', - }, - { - name: 'question.top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'question.type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The type of record being queried.', - example: 'AAAA', - }, - { - name: 'resolved_ip', - level: 'extended', - type: 'ip', - description: - 'Array containing all IPs seen in `answers.data`.\n\nThe `answers` array can be difficult to use, because of the variety of data\nformats it can contain. Extracting all IP addresses seen in there to `dns.resolved_ip`\nmakes it possible to index them as IP addresses, and makes them easier to\nvisualize and query for.', - example: ['10.10.10.10', '10.10.10.11'], - }, - { - name: 'response_code', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The DNS response code.', - example: 'NOERROR', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of DNS event captured, query or answer.\n\nIf your source of DNS events only gives you DNS queries, you should only create\ndns events of type `dns.type:query`.\n\nIf your source of DNS events gives you answers as well, you should create\none event per query (optionally as soon as the query is seen). And a second\nevent containing all query details as well as an array of answers.', - example: 'answer', - }, - ], - }, - { - name: 'ecs', - title: 'ECS', - group: 2, - description: 'Meta-information specific to ECS.', - type: 'group', - fields: [ - { - name: 'version', - level: 'core', - required: true, - type: 'keyword', - ignore_above: 1024, - description: - 'ECS version this event conforms to. `ecs.version` is a required\nfield and must exist in all events.\n\nWhen querying across multiple indices -- which may conform to slightly different\nECS versions -- this field lets integrations adjust to the schema version\nof the events.', - example: '1.0.0', - }, - ], - }, - { - name: 'error', - title: 'Error', - group: 2, - description: - 'These fields can represent errors of any kind.\n\nUse them for errors that happen while fetching events or in cases where the\nevent itself contains an error.', - type: 'group', - fields: [ - { - name: 'code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Error code describing the error.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the error.', - }, - { - name: 'message', - level: 'core', - type: 'text', - description: 'Error message.', - }, - { - name: 'stack_trace', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The stack trace of this error in plain text.', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The type of the error, for example the class name of the exception.', - example: 'java.lang.NullPointerException', - }, - ], - }, - { - name: 'event', - title: 'Event', - group: 2, - description: - 'The event fields are used for context information about the log\nor metric event itself.\n\nA log is defined as an event containing details of something that happened.\nLog events must include the time at which the thing happened. Examples of log\nevents include a process starting on a host, a network packet being sent from\na source to a destination, or a network connection between a client and a server\nbeing initiated or closed. A metric is defined as an event containing one or\nmore numerical measurements and the time at which the measurement was taken.\nExamples of metric events include memory pressure measured on a host and device\ntemperature. See the `event.kind` definition in this section for additional\ndetails about metric and state events.', - type: 'group', - fields: [ - { - name: 'action', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The action captured by the event.\n\nThis describes the information in the event. It is more specific than `event.category`.\nExamples are `group-add`, `process-started`, `file-created`. The value is\nnormally defined by the implementer.', - example: 'user-password-change', - }, - { - name: 'category', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nsecond level in the ECS category hierarchy.\n\n`event.category` represents the "big buckets" of ECS categories. For example,\nfiltering on `event.category:process` yields all events relating to process\nactivity. This field is closely related to `event.type`, which is used as\na subcategory.\n\nThis field is an array. This will allow proper categorization of some events\nthat fall in multiple categories.', - example: 'authentication', - }, - { - name: 'code', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Identification code for this event, if one exists.\n\nSome event sources use event codes to identify messages unambiguously, regardless\nof message language or wording adjustments over time. An example of this is\nthe Windows Event ID.', - example: 4648, - }, - { - name: 'created', - level: 'core', - type: 'date', - description: - 'event.created contains the date/time when the event was first\nread by an agent, or by your pipeline.\n\nThis field is distinct from @timestamp in that @timestamp typically contain\nthe time extracted from the original event.\n\nIn most situations, these two timestamps will be slightly different. The difference\ncan be used to calculate the delay between your source generating an event,\nand the time when your agent first processed it. This can be used to monitor\nyour agent or pipeline ability to keep up with your event source.\n\nIn case the two timestamps are identical, @timestamp should be used.', - example: '2016-05-23T08:05:34.857Z', - }, - { - name: 'dataset', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the dataset.\n\nIf an event source publishes more than one type of log or events (e.g. access\nlog, error log), the dataset is used to specify which one the event comes\nfrom.\n\nIt is recommended but not required to start the dataset name with the module\nname, followed by a dot, then the dataset name.', - example: 'apache.access', - }, - { - name: 'duration', - level: 'core', - type: 'long', - format: 'duration', - input_format: 'nanoseconds', - output_format: 'asMilliseconds', - output_precision: 1, - description: - 'Duration of the event in nanoseconds.\n\nIf event.start and event.end are known this value should be the difference\nbetween the end and start time.', - }, - { - name: 'end', - level: 'extended', - type: 'date', - description: - 'event.end contains the date when the event ended or when the activity\nwas last observed.', - }, - { - name: 'hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Hash (perhaps logstash fingerprint) of raw field to be able to\ndemonstrate log integrity.', - example: '123456789012345678901234567890ABCD', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique ID to describe the event.', - example: '8a4f500d', - }, - { - name: 'ingested', - level: 'core', - type: 'date', - description: - 'Timestamp when an event arrived in the central data store.\n\nThis is different from `@timestamp`, which is when the event originally occurred. It is\nalso different from `event.created`, which is meant to capture the first time\nan agent saw the event.\n\nIn normal conditions, assuming no tampering, the timestamps should chronologically\nlook like this: `@timestamp` < `event.created` < `event.ingested`.', - example: '2016-05-23T08:05:35.101Z', - default_field: false, - }, - { - name: 'kind', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nhighest level in the ECS category hierarchy.\n\n`event.kind` gives high-level information about what type of information the\nevent contains, without being specific to the contents of the event. For example,\nvalues of this field distinguish alert events from metric events.\n\nThe value of this field can be used to inform how these kinds of events should\nbe handled. They may warrant different retention, different access control,\nit may also help understand whether the data coming in at a regular interval\nor not.', - example: 'alert', - }, - { - name: 'module', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the module this data is coming from.\n\nIf your monitoring agent supports the concept of modules or plugins to process\nevents of a given source (e.g. Apache logs), `event.module` should contain\nthe name of this module.', - example: 'apache', - }, - { - name: 'original', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Raw text message of entire event. Used to demonstrate log integrity.\n\nThis field is not indexed and doc_values are disabled. It cannot be searched,\nbut it can be retrieved from `_source`.', - example: - 'Sep 19 08:26:10 host CEF:0|Security| threatmanager|1.0|100|\nworm successfully stopped|10|src=10.0.0.1 dst=2.1.2.2spt=1232', - }, - { - name: 'outcome', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nlowest level in the ECS category hierarchy.\n\n`event.outcome` simply denotes whether the event represents a success or a\nfailure from the perspective of the entity that produced the event.\n\nNote that when a single transaction is described in multiple events, each\nevent may populate different values of `event.outcome`, according to their\nperspective.\n\nAlso note that in the case of a compound event (a single event that contains\nmultiple logical events), this field should be populated with the value that\nbest captures the overall success or failure from the perspective of the event\nproducer.\n\nFurther note that not all events will have an associated outcome. For example,\nthis field is generally not populated for metric events, events with `event.type:info`,\nor any events for which an outcome does not make logical sense.', - example: 'success', - }, - { - name: 'provider', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Source of the event.\n\nEvent transports such as Syslog or the Windows Event Log typically mention\nthe source of an event. It can be the name of the software that generated\nthe event (e.g. Sysmon, httpd), or of a subsystem of the operating system\n(kernel, Microsoft-Windows-Security-Auditing).', - example: 'kernel', - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Reference URL linking to additional information about this event.\n\nThis URL links to a static definition of the this event. Alert events, indicated\nby `event.kind:alert`, are a common use case for this field.', - example: 'https://system.vendor.com/event/#0001234', - default_field: false, - }, - { - name: 'risk_score', - level: 'core', - type: 'float', - description: - "Risk score or priority of the event (e.g. security solutions).\nUse your system's original value here.", - }, - { - name: 'risk_score_norm', - level: 'extended', - type: 'float', - description: - 'Normalized risk score or priority of the event, on a scale of\n0 to 100.\n\nThis is mainly useful if you use more than one system that assigns risk scores,\nand you want to see a normalized value across all systems.', - }, - { - name: 'sequence', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Sequence number of the event.\n\nThe sequence number is a value published by some event sources, to make the\nexact ordering of events unambiguous, regardless of the timestamp precision.', - }, - { - name: 'severity', - level: 'core', - type: 'long', - format: 'string', - description: - 'The numeric severity of the event according to your event source.\n\nWhat the different severity values mean can be different between sources and\nuse cases. It is up to the implementer to make sure severities are consistent\nacross events from the same source.\n\nThe Syslog severity belongs in `log.syslog.severity.code`. `event.severity`\nis meant to represent the severity according to the event source (e.g. firewall,\nIDS). If the event source does not publish its own severity, you may optionally\ncopy the `log.syslog.severity.code` to `event.severity`.', - example: 7, - }, - { - name: 'start', - level: 'extended', - type: 'date', - description: - 'event.start contains the date when the event started or when the\nactivity was first observed.', - }, - { - name: 'timezone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'This field should be populated when the event timestamp does\nnot include timezone information already (e.g. default Syslog timestamps).\nIt is optional otherwise.\n\nAcceptable timezone formats are: a canonical ID (e.g. "Europe/Amsterdam"),\nabbreviated (e.g. "EST") or an HH:mm differential (e.g. "-05:00").', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is one of four ECS Categorization Fields, and indicates the\nthird level in the ECS category hierarchy.\n\n`event.type` represents a categorization "sub-bucket" that, when used along\nwith the `event.category` field values, enables filtering events down to a\nlevel appropriate for single visualization.\n\nThis field is an array. This will allow proper categorization of some events\nthat fall in multiple event types.', - }, - { - name: 'url', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'URL linking to an external system to continue investigation of\nthis event.\n\nThis URL links to another system where in-depth investigation of the specific\noccurence of this event can take place. Alert events, indicated by `event.kind:alert`,\nare a common use case for this field.', - example: 'https://mysystem.mydomain.com/alert/5271dedb-f5b0-4218-87f0-4ac4870a38fe', - default_field: false, - }, - ], - }, - { - name: 'file', - title: 'File', - group: 2, - description: - 'A file is defined as a set of information that has been created\non, or has existed on a filesystem.\n\nFile objects can be associated with host events, network events, and/or file\nevents (e.g., those produced by File Integrity Monitoring [FIM] products or\nservices). File fields provide details about the affected file associated with\nthe event or metric.', - type: 'group', - fields: [ - { - name: 'accessed', - level: 'extended', - type: 'date', - description: - 'Last time the file was accessed.\n\nNote that not all filesystems keep track of access time.', - }, - { - name: 'attributes', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of file attributes.\n\nAttributes names will vary by platform. Here is a non-exhaustive list of values\nthat are expected in this field: archive, compressed, directory, encrypted,\nexecute, hidden, read, readonly, system, write.', - example: '["readonly", "system"]', - default_field: false, - }, - { - name: 'code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'created', - level: 'extended', - type: 'date', - description: - 'File creation time.\n\nNote that not all filesystems store the creation time.', - }, - { - name: 'ctime', - level: 'extended', - type: 'date', - description: - 'Last time the file attributes or metadata changed.\n\nNote that changes to the file content will update `mtime`. This implies `ctime`\nwill be adjusted at the same time, since `mtime` is an attribute of the file.', - }, - { - name: 'device', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Device that is the source of the file.', - example: 'sda', - }, - { - name: 'directory', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Directory where the file is located. It should include the drive\nletter, when appropriate.', - example: '/home/alice', - }, - { - name: 'drive_letter', - level: 'extended', - type: 'keyword', - ignore_above: 1, - description: - 'Drive letter where the file is located. This field is only relevant\non Windows.\n\nThe value should be uppercase, and not include the colon.', - example: 'C', - default_field: false, - }, - { - name: 'extension', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'File extension.', - example: 'png', - }, - { - name: 'gid', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Primary group ID (GID) of the file.', - example: '1001', - }, - { - name: 'group', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Primary group name of the file.', - example: 'alice', - }, - { - name: 'hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - }, - { - name: 'hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - }, - { - name: 'hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - }, - { - name: 'hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - }, - { - name: 'inode', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Inode representing the file in the filesystem.', - example: '256383', - }, - { - name: 'mime_type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'MIME type should identify the format of the file or stream of bytes\nusing https://www.iana.org/assignments/media-types/media-types.xhtml[IANA\nofficial types], where possible. When more than one type is applicable, the\nmost specific type should be used.', - default_field: false, - }, - { - name: 'mode', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Mode of the file in octal representation.', - example: '0640', - }, - { - name: 'mtime', - level: 'extended', - type: 'date', - description: 'Last time the file content was modified.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the file including the extension, without the directory.', - example: 'example.png', - }, - { - name: 'owner', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: "File owner's username.", - example: 'alice', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'Full path to the file, including the file name. It should include\nthe drive letter, when appropriate.', - example: '/home/alice/example.png', - }, - { - name: 'pe.company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'pe.description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'pe.file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'pe.original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'pe.product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - { - name: 'size', - level: 'extended', - type: 'long', - description: 'File size in bytes.\n\nOnly relevant when `file.type` is "file".', - example: 16384, - }, - { - name: 'target_path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Target path for symlinks.', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'File type (file, dir, or symlink).', - example: 'file', - }, - { - name: 'uid', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The user ID (UID) or security identifier (SID) of the file owner.', - example: '1001', - }, - ], - }, - { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an\nevent.\n\nThis geolocation information can be derived from techniques such as Geo IP,\nor be user-supplied.', - type: 'group', - fields: [ - { - name: 'city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - ], - }, - { - name: 'group', - title: 'Group', - group: 2, - description: - 'The group fields are meant to represent groups that are relevant\nto the event.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - ], - }, - { - name: 'hash', - title: 'Hash', - group: 2, - description: - 'The hash fields represent different hash algorithms and their values.\n\nField names for common hashes (e.g. MD5, SHA1) are predefined. Add fields for\nother hashes by lowercasing the hash algorithm name and using underscore separators\nas appropriate (snake case, e.g. sha3_512).', - type: 'group', - fields: [ - { - name: 'md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - }, - { - name: 'sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - }, - { - name: 'sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - }, - { - name: 'sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - }, - ], - }, - { - name: 'host', - title: 'Host', - group: 2, - description: - 'A host is defined as a general computing instance.\n\nECS host.* fields should be populated with details about the host on which the\nevent happened, or from which the measurement was taken. Host types include\nhardware, virtual machines, Docker containers, and Kubernetes nodes.', - type: 'group', - fields: [ - { - name: 'architecture', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system architecture.', - example: 'x86_64', - }, - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the domain of which the host is a member.\n\nFor example, on Windows this could be the host Active Directory domain\nor NetBIOS domain name. For Linux this could be the domain of the host\nLDAP provider.', - example: 'CONTOSO', - default_field: false, - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'hostname', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Hostname of the host.\n\nIt normally contains what the `hostname` command returns on the host machine.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique host id.\n\nAs hostname is not always unique, use values that are meaningful in your environment.\n\nExample: The current usage of `beat.name`.', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'Host ip addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Host mac addresses.', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Type of host.\n\nFor Cloud providers this can be the machine type like `t2.medium`. If vm,\nthis could be the container, for example, or other information meaningful\nin your environment.', - }, - { - name: 'uptime', - level: 'extended', - type: 'long', - description: 'Seconds the host has been up.', - example: 1325, - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'http', - title: 'HTTP', - group: 2, - description: - 'Fields related to HTTP activity. Use the `url` field set to store\nthe url of the request.', - type: 'group', - fields: [ - { - name: 'request.body.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the request body.', - example: 887, - }, - { - name: 'request.body.content', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The full HTTP request body.', - example: 'Hello world', - }, - { - name: 'request.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the request (body and headers).', - example: 1437, - }, - { - name: 'request.method', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'HTTP request method.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'get, post, put', - }, - { - name: 'request.referrer', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Referrer for this HTTP request.', - example: 'https://blog.example.com/', - }, - { - name: 'response.body.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the response body.', - example: 887, - }, - { - name: 'response.body.content', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The full HTTP response body.', - example: 'Hello world', - }, - { - name: 'response.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the response (body and headers).', - example: 1437, - }, - { - name: 'response.status_code', - level: 'extended', - type: 'long', - format: 'string', - description: 'HTTP response status code.', - example: 404, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'HTTP version.', - example: 1.1, - }, - ], - }, - { - name: 'interface', - title: 'Interface', - group: 2, - description: - 'The interface fields are used to record ingress and egress interface\ninformation when reported by an observer (e.g. firewall, router, load balancer)\nin the context of the observer handling a network connection. In the case of\na single observer interface (e.g. network sensor on a span port) only the observer.ingress\ninformation should be populated.', - type: 'group', - fields: [ - { - name: 'alias', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Interface alias as reported by the system, typically used in firewall\nimplementations for e.g. inside, outside, or dmz logical interface naming.', - example: 'outside', - default_field: false, - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface ID as reported by an observer (typically SNMP interface\nID).', - example: 10, - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface name as reported by the system.', - example: 'eth0', - default_field: false, - }, - ], - }, - { - name: 'log', - title: 'Log', - group: 2, - description: - 'Details about the event logging mechanism or logging transport.\n\nThe log.* fields are typically populated with details about the logging mechanism\nused to create and/or transport the event. For example, syslog details belong\nunder `log.syslog.*`.\n\nThe details specific to your event source are typically not logged under `log.*`,\nbut rather in `event.*` or in other ECS fields.', - type: 'group', - fields: [ - { - name: 'level', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Original log level of the log event.\n\nIf the source of the event provides a log level or textual severity, this\nis the one that goes in `log.level`. If your source does not specify one,\nyou may put your event transport severity here (e.g. Syslog severity).\n\nSome examples are `warn`, `err`, `i`, `informational`.', - example: 'error', - }, - { - name: 'logger', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The name of the logger inside an application. This is usually the\nname of the class which initialized the logger, or can be a custom name.', - example: 'org.elasticsearch.bootstrap.Bootstrap', - }, - { - name: 'origin.file.line', - level: 'extended', - type: 'integer', - description: - 'The line number of the file containing the source code which originated\nthe log event.', - example: 42, - }, - { - name: 'origin.file.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The name of the file containing the source code which originated\nthe log event. Note that this is not the name of the log file.', - example: 'Bootstrap.java', - }, - { - name: 'origin.function', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The name of the function or method which originated the log event.', - example: 'init', - }, - { - name: 'original', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'This is the original log message and contains the full log message\nbefore splitting it up in multiple parts.\n\nIn contrast to the `message` field which can contain an extracted part of\nthe log message, this field contains the original, full log message. It can\nhave already some modifications applied like encoding or new lines removed\nto clean up the log message.\n\nThis field is not indexed and doc_values are disabled so it can not be queried\nbut the value can be retrieved from `_source`.', - example: 'Sep 19 08:26:10 localhost My log', - }, - { - name: 'syslog', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'The Syslog metadata of the event, if the event was transmitted\nvia Syslog. Please see RFCs 5424 or 3164.', - }, - { - name: 'syslog.facility.code', - level: 'extended', - type: 'long', - format: 'string', - description: - 'The Syslog numeric facility of the log event, if available.\n\nAccording to RFCs 5424 and 3164, this value should be an integer between 0\nand 23.', - example: 23, - }, - { - name: 'syslog.facility.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The Syslog text-based facility of the log event, if available.', - example: 'local7', - }, - { - name: 'syslog.priority', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Syslog numeric priority of the event, if available.\n\nAccording to RFCs 5424 and 3164, the priority is 8 * facility + severity.\nThis number is therefore expected to contain a value between 0 and 191.', - example: 135, - }, - { - name: 'syslog.severity.code', - level: 'extended', - type: 'long', - description: - 'The Syslog numeric severity of the log event, if available.\n\nIf the event source publishing via Syslog provides a different numeric severity\nvalue (e.g. firewall, IDS), your source numeric severity should go to `event.severity`.\nIf the event source does not specify a distinct severity, you can optionally\ncopy the Syslog severity to `event.severity`.', - example: 3, - }, - { - name: 'syslog.severity.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The Syslog numeric severity of the log event, if available.\n\nIf the event source publishing via Syslog provides a different severity value\n(e.g. firewall, IDS), your source text severity should go to `log.level`.\nIf the event source does not specify a distinct severity, you can optionally\ncopy the Syslog severity to `log.level`.', - example: 'Error', - }, - ], - }, - { - name: 'network', - title: 'Network', - group: 2, - description: - 'The network is defined as the communication path over which a host\nor network event happens.\n\nThe network.* fields should be populated with details about the network activity\nassociated with an event.', - type: 'group', - fields: [ - { - name: 'application', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A name given to an application level protocol. This can be arbitrarily\nassigned for things like microservices, but also apply to things like skype,\nicq, facebook, twitter. This would be used in situations where the vendor\nor service can be decoded such as from the source/dest IP owners, ports, or\nwire format.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'aim', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: - 'Total bytes transferred in both directions.\n\nIf `source.bytes` and `destination.bytes` are known, `network.bytes` is their\nsum.', - example: 368, - }, - { - name: 'community_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash of source and destination IPs and ports, as well as the\nprotocol used in a communication. This is a tool-agnostic standard to identify\nflows.\n\nLearn more at https://github.com/corelight/community-id-spec.', - example: '1:hO+sN4H+MG5MY/8hIrXPqc4ZQz0=', - }, - { - name: 'direction', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - "Direction of the network traffic.\nRecommended values are:\n * inbound\n * outbound\n * internal\n * external\n * unknown\n\nWhen mapping events from a host-based monitoring context, populate this field from the host's point of view.\nWhen mapping events from a network or perimeter-based monitoring context, populate this field from the point of view of your network perimeter.", - example: 'inbound', - }, - { - name: 'forwarded_ip', - level: 'core', - type: 'ip', - description: 'Host IP address when the source IP address is the proxy.', - example: '192.1.1.2', - }, - { - name: 'iana_number', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'IANA Protocol Number (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml).\nStandardized list of protocols. This aligns well with NetFlow and sFlow related\nlogs which use the IANA Protocol Number.', - example: 6, - }, - { - name: 'inner', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'Network.inner fields are added in addition to network.vlan fields\nto describe the innermost VLAN when q-in-q VLAN tagging is present. Allowed\nfields include vlan.id and vlan.name. Inner vlan fields are typically used\nwhen sending traffic with multiple 802.1q encapsulations to a network sensor\n(e.g. Zeek, Wireshark.)', - default_field: false, - }, - { - name: 'inner.vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'inner.vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name given by operators to sections of their network.', - example: 'Guest Wifi', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: - 'Total packets transferred in both directions.\n\nIf `source.packets` and `destination.packets` are known, `network.packets`\nis their sum.', - example: 24, - }, - { - name: 'protocol', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'L7 Network protocol name. ex. http, lumberjack, transport protocol.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'http', - }, - { - name: 'transport', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Same as network.iana_number, but instead using the Keyword name\nof the transport layer (udp, tcp, ipv6-icmp, etc.)\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'tcp', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'In the OSI Model this would be the Network Layer. ipv4, ipv6,\nipsec, pim, etc\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'ipv4', - }, - { - name: 'vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - ], - }, - { - name: 'observer', - title: 'Observer', - group: 2, - description: - 'An observer is defined as a special network, security, or application\ndevice used to detect, observe, or create network, security, or application-related\nevents and metrics.\n\nThis could be a custom hardware appliance or a server that has been configured\nto run special network, security, or application software. Examples include\nfirewalls, web proxies, intrusion detection/prevention systems, network monitoring\nsensors, web application firewalls, data loss prevention systems, and APM servers.\nThe observer.* fields shall be populated with details of the system, if any,\nthat detects, observes and/or creates a network, security, or application event\nor metric. Message queues and ETL components used in processing events or metrics\nare not considered observers in ECS.', - type: 'group', - fields: [ - { - name: 'egress', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'Observer.egress holds information like interface number and name,\nvlan, and zone information to classify egress traffic. Single armed monitoring\nsuch as a network sensor on a span port should only use observer.ingress\nto categorize traffic.', - default_field: false, - }, - { - name: 'egress.interface.alias', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Interface alias as reported by the system, typically used in firewall\nimplementations for e.g. inside, outside, or dmz logical interface naming.', - example: 'outside', - default_field: false, - }, - { - name: 'egress.interface.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface ID as reported by an observer (typically SNMP interface\nID).', - example: 10, - default_field: false, - }, - { - name: 'egress.interface.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface name as reported by the system.', - example: 'eth0', - default_field: false, - }, - { - name: 'egress.vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'egress.vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - { - name: 'egress.zone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Network zone of outbound traffic as reported by the observer to\ncategorize the destination area of egress traffic, e.g. Internal, External,\nDMZ, HR, Legal, etc.', - example: 'Public_Internet', - default_field: false, - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'hostname', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Hostname of the observer.', - }, - { - name: 'ingress', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: - 'Observer.ingress holds information like interface number and name,\nvlan, and zone information to classify ingress traffic. Single armed monitoring\nsuch as a network sensor on a span port should only use observer.ingress\nto categorize traffic.', - default_field: false, - }, - { - name: 'ingress.interface.alias', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Interface alias as reported by the system, typically used in firewall\nimplementations for e.g. inside, outside, or dmz logical interface naming.', - example: 'outside', - default_field: false, - }, - { - name: 'ingress.interface.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface ID as reported by an observer (typically SNMP interface\nID).', - example: 10, - default_field: false, - }, - { - name: 'ingress.interface.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Interface name as reported by the system.', - example: 'eth0', - default_field: false, - }, - { - name: 'ingress.vlan.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'ingress.vlan.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - { - name: 'ingress.zone', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Network zone of incoming traffic as reported by the observer to\ncategorize the source area of ingress traffic. e.g. internal, External, DMZ,\nHR, Legal, etc.', - example: 'DMZ', - default_field: false, - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'IP addresses of the observer.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC addresses of the observer', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Custom name of the observer.\n\nThis is a name that can be given to an observer. This can be helpful for example\nif multiple firewalls of the same model are used in an organization.\n\nIf no custom name is needed, the field can be left empty.', - example: '1_proxySG', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The product name of the observer.', - example: 's200', - }, - { - name: 'serial_number', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Observer serial number.', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of the observer the data is coming from.\n\nThere is no predefined list of observer types. Some examples are `forwarder`,\n`firewall`, `ids`, `ips`, `proxy`, `poller`, `sensor`, `APM server`.', - example: 'firewall', - }, - { - name: 'vendor', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Vendor name of the observer.', - example: 'Symantec', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Observer version.', - }, - ], - }, - { - name: 'organization', - title: 'Organization', - group: 2, - description: - 'The organization fields enrich data with information about the company\nor entity the data is associated with.\n\nThese fields help you arrange or filter data stored in an index by one or multiple\norganizations.', - type: 'group', - fields: [ - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the organization.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - }, - ], - }, - { - name: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - type: 'group', - fields: [ - { - name: 'family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - ], - }, - { - name: 'package', - title: 'Package', - group: 2, - description: - 'These fields contain information about an installed software package.\nIt contains general information about a package, such as name, version or size.\nIt also contains installation details, such as time or location.', - type: 'group', - fields: [ - { - name: 'architecture', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Package architecture.', - example: 'x86_64', - }, - { - name: 'build_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the build version of the installed\npackage.\n\nFor example use the commit SHA of a non-released package.', - example: '36f4f7e89dd61b0988b12ee000b98966867710cd', - default_field: false, - }, - { - name: 'checksum', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Checksum of the installed package for verification.', - example: '68b329da9893e34099c7d8ad5cb9c940', - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Description of the package.', - example: - 'Open source programming language to build simple/reliable/efficient\nsoftware.', - }, - { - name: 'install_scope', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Indicating how the package was installed, e.g. user-local, global.', - example: 'global', - }, - { - name: 'installed', - level: 'extended', - type: 'date', - description: 'Time when package was installed.', - }, - { - name: 'license', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'License under which the package was released.\n\nUse a short name, e.g. the license identifier from SPDX License List where\npossible (https://spdx.org/licenses/).', - example: 'Apache License 2.0', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Package name', - example: 'go', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Path where the package is installed.', - example: '/usr/local/Cellar/go/1.12.9/', - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Home page or reference URL of the software in this package, if\navailable.', - example: 'https://golang.org', - default_field: false, - }, - { - name: 'size', - level: 'extended', - type: 'long', - format: 'string', - description: 'Package size in bytes.', - example: 62231, - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Type of package.\n\nThis should contain the package file type, rather than the package manager\nname. Examples: rpm, dpkg, brew, npm, gem, nupkg, jar.', - example: 'rpm', - default_field: false, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Package version', - example: '1.12.9', - }, - ], - }, - { - name: 'pe', - title: 'PE Header', - group: 2, - description: 'These fields contain Windows Portable Executable (PE) metadata.', - type: 'group', - fields: [ - { - name: 'company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - ], - }, - { - name: 'process', - title: 'Process', - group: 2, - description: - 'These fields contain information about a process.\n\nThese fields can help you correlate metrics information with a process id/name\nfrom a log message. The `process.pid` often stays in the metric itself and\nis copied to the global field for correlation.', - type: 'group', - fields: [ - { - name: 'args', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.', - example: ['/usr/bin/ssh', '-l', 'user', '10.0.0.16'], - }, - { - name: 'args_count', - level: 'extended', - type: 'long', - description: - 'Length of the process.args array.\n\nThis field can be useful for querying or performing bucket analysis on how\nmany arguments were provided to start a process. More arguments may be an\nindication of suspicious activity.', - example: 4, - default_field: false, - }, - { - name: 'code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'command_line', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'Full command line that started the process, including the absolute\npath to the executable, and all arguments.\n\nSome arguments may be filtered to protect sensitive information.', - example: '/usr/bin/ssh -l user 10.0.0.16', - default_field: false, - }, - { - name: 'entity_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier for the process.\n\nThe implementation of this is specified by the data source, but some examples\nof what could be used here are a process-generated UUID, Sysmon Process GUIDs,\nor a hash of some uniquely identifying components of a process.\n\nConstructing a globally unique identifier is a common practice to mitigate\nPID reuse as well as to identify a specific process over time, across multiple\nmonitored hosts.', - example: 'c2c455d9f99375d', - default_field: false, - }, - { - name: 'executable', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', - }, - { - name: 'exit_code', - level: 'extended', - type: 'long', - description: - 'The exit code of the process, if this is a termination event.\n\nThe field should be absent if there is no exit code for the event (e.g. process\nstart).', - example: 137, - default_field: false, - }, - { - name: 'hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - }, - { - name: 'hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - }, - { - name: 'hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - }, - { - name: 'hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Process name.\n\nSometimes called program name or similar.', - example: 'ssh', - }, - { - name: 'parent.args', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of process arguments.\n\nMay be filtered to protect sensitive information.', - example: ['ssh', '-l', 'user', '10.0.0.16'], - default_field: false, - }, - { - name: 'parent.args_count', - level: 'extended', - type: 'long', - description: - 'Length of the process.args array.\n\nThis field can be useful for querying or performing bucket analysis on how\nmany arguments were provided to start a process. More arguments may be an\nindication of suspicious activity.', - example: 4, - default_field: false, - }, - { - name: 'parent.code_signature.exists', - level: 'core', - type: 'boolean', - description: 'Boolean to capture if a signature is present.', - example: 'true', - default_field: false, - }, - { - name: 'parent.code_signature.status', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Additional information about the certificate status.\n\nThis is useful for logging cryptographic errors with the certificate validity\nor trust status. Leave unpopulated if the validity or trust of the certificate\nwas unchecked.', - example: 'ERROR_UNTRUSTED_ROOT', - default_field: false, - }, - { - name: 'parent.code_signature.subject_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Subject name of the code signer', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'parent.code_signature.trusted', - level: 'extended', - type: 'boolean', - description: - 'Stores the trust status of the certificate chain.\n\nValidating the trust of the certificate chain may be complicated, and this\nfield should only be populated by tools that actively check the status.', - example: 'true', - default_field: false, - }, - { - name: 'parent.code_signature.valid', - level: 'extended', - type: 'boolean', - description: - 'Boolean to capture if the digital signature is verified against\nthe binary content.\n\nLeave unpopulated if a certificate was unchecked.', - example: 'true', - default_field: false, - }, - { - name: 'parent.command_line', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'Full command line that started the process, including the absolute\npath to the executable, and all arguments.\n\nSome arguments may be filtered to protect sensitive information.', - example: '/usr/bin/ssh -l user 10.0.0.16', - default_field: false, - }, - { - name: 'parent.entity_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier for the process.\n\nThe implementation of this is specified by the data source, but some examples\nof what could be used here are a process-generated UUID, Sysmon Process GUIDs,\nor a hash of some uniquely identifying components of a process.\n\nConstructing a globally unique identifier is a common practice to mitigate\nPID reuse as well as to identify a specific process over time, across multiple\nmonitored hosts.', - example: 'c2c455d9f99375d', - default_field: false, - }, - { - name: 'parent.executable', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', - default_field: false, - }, - { - name: 'parent.exit_code', - level: 'extended', - type: 'long', - description: - 'The exit code of the process, if this is a termination event.\n\nThe field should be absent if there is no exit code for the event (e.g. process\nstart).', - example: 137, - default_field: false, - }, - { - name: 'parent.hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'MD5 hash.', - default_field: false, - }, - { - name: 'parent.hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA1 hash.', - default_field: false, - }, - { - name: 'parent.hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA256 hash.', - default_field: false, - }, - { - name: 'parent.hash.sha512', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'SHA512 hash.', - default_field: false, - }, - { - name: 'parent.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'Process name.\n\nSometimes called program name or similar.', - example: 'ssh', - default_field: false, - }, - { - name: 'parent.pgid', - level: 'extended', - type: 'long', - format: 'string', - description: 'Identifier of the group of processes the process belongs to.', - default_field: false, - }, - { - name: 'parent.pid', - level: 'core', - type: 'long', - format: 'string', - description: 'Process id.', - example: 4242, - default_field: false, - }, - { - name: 'parent.ppid', - level: 'extended', - type: 'long', - format: 'string', - description: "Parent process' pid.", - example: 4241, - default_field: false, - }, - { - name: 'parent.start', - level: 'extended', - type: 'date', - description: 'The time the process started.', - example: '2016-05-23T08:05:34.853Z', - default_field: false, - }, - { - name: 'parent.thread.id', - level: 'extended', - type: 'long', - format: 'string', - description: 'Thread ID.', - example: 4242, - default_field: false, - }, - { - name: 'parent.thread.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Thread name.', - example: 'thread-0', - default_field: false, - }, - { - name: 'parent.title', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'Process title.\n\nThe proctitle, some times the same as process name. Can also be different:\nfor example a browser setting its title to the web page currently opened.', - default_field: false, - }, - { - name: 'parent.uptime', - level: 'extended', - type: 'long', - description: 'Seconds the process has been up.', - example: 1325, - default_field: false, - }, - { - name: 'parent.working_directory', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'The working directory of the process.', - example: '/home/alice', - default_field: false, - }, - { - name: 'pe.company', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal company name of the file, provided at compile-time.', - example: 'Microsoft Corporation', - default_field: false, - }, - { - name: 'pe.description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal description of the file, provided at compile-time.', - example: 'Paint', - default_field: false, - }, - { - name: 'pe.file_version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal version of the file, provided at compile-time.', - example: '6.3.9600.17415', - default_field: false, - }, - { - name: 'pe.original_file_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal name of the file, provided at compile-time.', - example: 'MSPAINT.EXE', - default_field: false, - }, - { - name: 'pe.product', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Internal product name of the file, provided at compile-time.', - example: 'Microsoft® Windows® Operating System', - default_field: false, - }, - { - name: 'pgid', - level: 'extended', - type: 'long', - format: 'string', - description: 'Identifier of the group of processes the process belongs to.', - }, - { - name: 'pid', - level: 'core', - type: 'long', - format: 'string', - description: 'Process id.', - example: 4242, - }, - { - name: 'ppid', - level: 'extended', - type: 'long', - format: 'string', - description: "Parent process' pid.", - example: 4241, - }, - { - name: 'start', - level: 'extended', - type: 'date', - description: 'The time the process started.', - example: '2016-05-23T08:05:34.853Z', - }, - { - name: 'thread.id', - level: 'extended', - type: 'long', - format: 'string', - description: 'Thread ID.', - example: 4242, - }, - { - name: 'thread.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Thread name.', - example: 'thread-0', - }, - { - name: 'title', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'Process title.\n\nThe proctitle, some times the same as process name. Can also be different:\nfor example a browser setting its title to the web page currently opened.', - }, - { - name: 'uptime', - level: 'extended', - type: 'long', - description: 'Seconds the process has been up.', - example: 1325, - }, - { - name: 'working_directory', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'The working directory of the process.', - example: '/home/alice', - }, - ], - }, - { - name: 'registry', - title: 'Registry', - group: 2, - description: 'Fields related to Windows Registry operations.', - type: 'group', - fields: [ - { - name: 'data.bytes', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Original bytes written with base64 encoding.\n\nFor Windows registry operations, such as SetValueEx and RegQueryValueEx, this\ncorresponds to the data pointed by `lp_data`. This is optional but provides\nbetter recoverability and should be populated for REG_BINARY encoded values.', - example: 'ZQBuAC0AVQBTAAAAZQBuAAAAAAA=', - default_field: false, - }, - { - name: 'data.strings', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Content when writing string types.\n\nPopulated as an array when writing string data to the registry. For single\nstring registry types (REG_SZ, REG_EXPAND_SZ), this should be an array with\none string. For sequences of string with REG_MULTI_SZ, this array will be\nvariable length. For numeric data, such as REG_DWORD and REG_QWORD, this should\nbe populated with the decimal representation (e.g `"1"`).', - example: '["C:\\rta\\red_ttp\\bin\\myapp.exe"]', - default_field: false, - }, - { - name: 'data.type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Standard registry type for encoding contents', - example: 'REG_SZ', - default_field: false, - }, - { - name: 'hive', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Abbreviated name for the hive.', - example: 'HKLM', - default_field: false, - }, - { - name: 'key', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Hive-relative path of keys.', - example: - 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\winword.exe', - default_field: false, - }, - { - name: 'path', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Full path, including hive, key and value', - example: - 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution\nOptions\\winword.exe\\Debugger', - default_field: false, - }, - { - name: 'value', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the value written.', - example: 'Debugger', - default_field: false, - }, - ], - }, - { - name: 'related', - title: 'Related', - group: 2, - description: - 'This field set is meant to facilitate pivoting around a piece of\ndata.\n\nSome pieces of information can be seen in many places in an ECS event. To facilitate\nsearching for them, store an array of all seen values to their corresponding\nfield in `related.`.\n\nA concrete example is IP addresses, which can be under host, observer, source,\ndestination, client, server, and network.forwarded_ip. If you append all IPs\nto `related.ip`, you can then search for a given IP trivially, no matter where\nit appeared, by querying `related.ip:192.0.2.15`.', - type: 'group', - fields: [ - { - name: 'hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - "All the hashes seen on your event. Populating this field, then\nusing it to search for hashes can help in situations where you're unsure what\nthe hash algorithm is (and therefore which key name to search).", - default_field: false, - }, - { - name: 'ip', - level: 'extended', - type: 'ip', - description: 'All of the IPs seen on your event.', - }, - { - name: 'user', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'All the user names seen on your event.', - default_field: false, - }, - ], - }, - { - name: 'rule', - title: 'Rule', - group: 2, - description: - 'Rule fields are used to capture the specifics of any observer or\nagent rules that generate alerts or other notable events.\n\nExamples of data sources that would populate the rule fields include: network\nadmission control platforms, network or host IDS/IPS, network firewalls, web\napplication firewalls, url filters, endpoint detection and response (EDR) systems,\netc.', - type: 'group', - fields: [ - { - name: 'author', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name, organization, or pseudonym of the author or authors who created\nthe rule used to generate this event.', - example: ['Star-Lord'], - default_field: false, - }, - { - name: 'category', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A categorization value keyword used by the entity using the rule\nfor detection of this event.', - example: 'Attempted Information Leak', - default_field: false, - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The description of the rule generating the event.', - example: 'Block requests to public DNS over HTTPS / TLS protocols', - default_field: false, - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A rule ID that is unique within the scope of an agent, observer,\nor other entity using the rule for detection of this event.', - example: 101, - default_field: false, - }, - { - name: 'license', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the license under which the rule used to generate this\nevent is made available.', - example: 'Apache 2.0', - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The name of the rule or signature generating the event.', - example: 'BLOCK_DNS_over_TLS', - default_field: false, - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Reference URL to additional information about the rule used to\ngenerate this event.\n\nThe URL can point to the vendor documentation about the rule. If that is\nnot available, it can also be a link to a more general page describing this\ntype of alert.', - example: 'https://en.wikipedia.org/wiki/DNS_over_TLS', - default_field: false, - }, - { - name: 'ruleset', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the ruleset, policy, group, or parent category in which\nthe rule used to generate this event is a member.', - example: 'Standard_Protocol_Filters', - default_field: false, - }, - { - name: 'uuid', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A rule ID that is unique within the scope of a set or group of\nagents, observers, or other entities using the rule for detection of this\nevent.', - example: 1100110011, - default_field: false, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The version / revision of the rule being used for analysis.', - example: 1.1, - default_field: false, - }, - ], - }, - { - name: 'server', - title: 'Server', - group: 2, - description: - 'A Server is defined as the responder in a network connection for\nevents regarding sessions, connections, or bidirectional flow records.\n\nFor TCP events, the server is the receiver of the initial SYN packet(s) of the\nTCP connection. For other protocols, the server is generally the responder in\nthe network transaction. Some systems actually use the term "responder" to refer\nthe server in TCP connections. The server fields describe details about the\nsystem acting as the server in the network event. Server fields are usually\npopulated in conjunction with client fields. Server fields are generally not\npopulated for packet-level events.\n\nClient / server representations can add semantic context to an exchange, which\nis helpful to visualize the data in certain situations. If your context falls\nin that category, you should still ensure that source and destination are filled\nappropriately.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event server addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the server to the client.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Server domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the server.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the server.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated ip of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Translated port of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the server to the client.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the server.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered server domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'service', - title: 'Service', - group: 2, - description: - 'The service fields describe the service for or from which the data\nwas collected.\n\nThese fields help you find and correlate logs for a specific service and version.', - type: 'group', - fields: [ - { - name: 'ephemeral_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Ephemeral identifier of this service (if one exists).\n\nThis id normally changes across restarts, but `service.id` does not.', - example: '8a4f500f', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of the running service. If the service is comprised\nof many nodes, the `service.id` should be the same for all nodes.\n\nThis id should uniquely identify the service. This makes it possible to correlate\nlogs and metrics for one specific service, no matter which particular node\nemitted the event.\n\nNote that if you need to see the events from one specific host of the service,\nyou should filter on that `host.name` or `host.id` instead.', - example: 'd37e5ebfe0ae6c4972dbe9f0174a1637bb8247f6', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the service data is collected from.\n\nThe name of the service is normally user given. This allows for distributed\nservices that run on multiple hosts to correlate the related instances based\non the name.\n\nIn the case of Elasticsearch the `service.name` could contain the cluster\nname. For Beats the `service.name` is by default a copy of the `service.type`\nfield if no name is specified.', - example: 'elasticsearch-metrics', - }, - { - name: 'node.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of a service node.\n\nThis allows for two nodes of the same service running on the same host to\nbe differentiated. Therefore, `service.node.name` should typically be unique\nacross nodes of a given service.\n\nIn the case of Elasticsearch, the `service.node.name` could contain the unique\nnode name within the Elasticsearch cluster. In cases where the service doe not\nhave the concept of a node name, the host name or container name can be used\nto distinguish running instances that make up this service. If those do not\nprovide uniqueness (e.g. multiple instances of the service running on the\nsame host) - the node name can be manually set.', - example: 'instance-0000000016', - }, - { - name: 'state', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Current state of the service.', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of the service data is collected from.\n\nThe type can be used to group and correlate logs and metrics from one service\ntype.\n\nExample: If logs or metrics are collected from Elasticsearch, `service.type`\nwould be `elasticsearch`.', - example: 'elasticsearch', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: - 'Version of the service the data was collected from.\n\nThis allows to look at a data set only for a specific version of a service.', - example: '3.2.4', - }, - ], - }, - { - name: 'source', - title: 'Source', - group: 2, - description: - 'Source fields describe details about the source of a packet/event.\n\nSource fields are usually populated in conjunction with destination fields.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Some event source addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'as.number', - level: 'extended', - type: 'long', - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - }, - { - name: 'as.organization.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Organization name.', - example: 'Google LLC', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the source to the destination.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Source domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the source.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'MAC address of the source.', - }, - { - name: 'nat.ip', - level: 'extended', - type: 'ip', - description: - 'Translated ip of source based NAT sessions (e.g. internal client\nto internet)\n\nTypically connections traversing load balancers, firewalls, or routers.', - }, - { - name: 'nat.port', - level: 'extended', - type: 'long', - format: 'string', - description: - 'Translated port of source based NAT sessions. (e.g. internal client\nto internet)\n\nTypically used with load balancers, firewalls, or routers.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the source to the destination.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the source.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered source domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'user.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'threat', - title: 'Threat', - group: 2, - description: - 'Fields to classify events and alerts according to a threat taxonomy\nsuch as the Mitre ATT&CK framework.\n\nThese fields are for users to classify alerts from all of their sources (e.g.\nIDS, NGFW, etc.) within a common taxonomy. The threat.tactic.* are meant to\ncapture the high level category of the threat (e.g. "impact"). The threat.technique.*\nfields are meant to capture which kind of approach is used by this detected\nthreat, to accomplish the goal (e.g. "endpoint denial of service").', - type: 'group', - fields: [ - { - name: 'framework', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the threat framework used to further categorize and classify\nthe tactic and technique of the reported threat. Framework classification\ncan be provided by detecting systems, evaluated at ingest time, or retrospectively\ntagged to events.', - example: 'MITRE ATT&CK', - }, - { - name: 'tactic.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The id of tactic used by this threat. You can use the Mitre ATT&CK\nMatrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/\n)', - example: 'TA0040', - }, - { - name: 'tactic.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the type of tactic used by this threat. You can use the\nMitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/\n)', - example: 'impact', - }, - { - name: 'tactic.reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The reference url of tactic used by this threat. You can use the\nMitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/\n)', - example: 'https://attack.mitre.org/tactics/TA0040/', - }, - { - name: 'technique.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The id of technique used by this tactic. You can use the Mitre\nATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/\n)', - example: 'T1499', - }, - { - name: 'technique.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'The name of technique used by this tactic. You can use the Mitre\nATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/\n)', - example: 'endpoint denial of service', - }, - { - name: 'technique.reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The reference url of technique used by this tactic. You can use\nthe Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/\n)', - example: 'https://attack.mitre.org/techniques/T1499/', - }, - ], - }, - { - name: 'tls', - title: 'TLS', - group: 2, - description: - 'Fields related to a TLS connection. These fields focus on the TLS\nprotocol itself and intentionally avoids in-depth analysis of the related x.509\ncertificate files.', - type: 'group', - fields: [ - { - name: 'cipher', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'String indicating the cipher used during the current connection.', - example: 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256', - default_field: false, - }, - { - name: 'client.certificate', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'PEM-encoded stand-alone certificate offered by the client. This\nis usually mutually-exclusive of `client.certificate_chain` since this value\nalso exists in that list.', - example: 'MII...', - default_field: false, - }, - { - name: 'client.certificate_chain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of PEM-encoded certificates that make up the certificate\nchain offered by the client. This is usually mutually-exclusive of `client.certificate`\nsince that value should be the first certificate in the chain.', - example: ['MII...', 'MII...'], - default_field: false, - }, - { - name: 'client.hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the MD5 digest of DER-encoded version\nof certificate offered by the client. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC', - default_field: false, - }, - { - name: 'client.hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA1 digest of DER-encoded version\nof certificate offered by the client. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '9E393D93138888D288266C2D915214D1D1CCEB2A', - default_field: false, - }, - { - name: 'client.hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA256 digest of DER-encoded\nversion of certificate offered by the client. For consistency with other hash\nvalues, this value should be formatted as an uppercase hash.', - example: '0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0', - default_field: false, - }, - { - name: 'client.issuer', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Distinguished name of subject of the issuer of the x.509 certificate\npresented by the client.', - example: 'CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'client.ja3', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash that identifies clients based on how they perform an SSL/TLS\nhandshake.', - example: 'd4e5b18d6b55c71272893221c96ba240', - default_field: false, - }, - { - name: 'client.not_after', - level: 'extended', - type: 'date', - description: - 'Date/Time indicating when client certificate is no longer considered\nvalid.', - example: '2021-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'client.not_before', - level: 'extended', - type: 'date', - description: 'Date/Time indicating when client certificate is first considered\nvalid.', - example: '1970-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'client.server_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Also called an SNI, this tells the server which hostname to which\nthe client is attempting to connect. When this value is available, it should\nget copied to `destination.domain`.', - example: 'www.elastic.co', - default_field: false, - }, - { - name: 'client.subject', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Distinguished name of subject of the x.509 certificate presented\nby the client.', - example: 'CN=myclient, OU=Documentation Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'client.supported_ciphers', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Array of ciphers offered by the client during the client hello.', - example: [ - 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384', - 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384', - '...', - ], - default_field: false, - }, - { - name: 'curve', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'String indicating the curve used for the given cipher, when applicable.', - example: 'secp256r1', - default_field: false, - }, - { - name: 'established', - level: 'extended', - type: 'boolean', - description: - 'Boolean flag indicating if the TLS negotiation was successful and\ntransitioned to an encrypted tunnel.', - default_field: false, - }, - { - name: 'next_protocol', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'String indicating the protocol being tunneled. Per the values in\nthe IANA registry (https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids),\nthis string should be lower case.', - example: 'http/1.1', - default_field: false, - }, - { - name: 'resumed', - level: 'extended', - type: 'boolean', - description: - 'Boolean flag indicating if this TLS connection was resumed from\nan existing TLS negotiation.', - default_field: false, - }, - { - name: 'server.certificate', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'PEM-encoded stand-alone certificate offered by the server. This\nis usually mutually-exclusive of `server.certificate_chain` since this value\nalso exists in that list.', - example: 'MII...', - default_field: false, - }, - { - name: 'server.certificate_chain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Array of PEM-encoded certificates that make up the certificate\nchain offered by the server. This is usually mutually-exclusive of `server.certificate`\nsince that value should be the first certificate in the chain.', - example: ['MII...', 'MII...'], - default_field: false, - }, - { - name: 'server.hash.md5', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the MD5 digest of DER-encoded version\nof certificate offered by the server. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC', - default_field: false, - }, - { - name: 'server.hash.sha1', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA1 digest of DER-encoded version\nof certificate offered by the server. For consistency with other hash values,\nthis value should be formatted as an uppercase hash.', - example: '9E393D93138888D288266C2D915214D1D1CCEB2A', - default_field: false, - }, - { - name: 'server.hash.sha256', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Certificate fingerprint using the SHA256 digest of DER-encoded\nversion of certificate offered by the server. For consistency with other hash\nvalues, this value should be formatted as an uppercase hash.', - example: '0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0', - default_field: false, - }, - { - name: 'server.issuer', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Subject of the issuer of the x.509 certificate presented by the\nserver.', - example: 'CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'server.ja3s', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A hash that identifies servers based on how they perform an SSL/TLS\nhandshake.', - example: '394441ab65754e2207b1e1b457b3641d', - default_field: false, - }, - { - name: 'server.not_after', - level: 'extended', - type: 'date', - description: - 'Timestamp indicating when server certificate is no longer considered\nvalid.', - example: '2021-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'server.not_before', - level: 'extended', - type: 'date', - description: 'Timestamp indicating when server certificate is first considered\nvalid.', - example: '1970-01-01T00:00:00.000Z', - default_field: false, - }, - { - name: 'server.subject', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Subject of the x.509 certificate presented by the server.', - example: 'CN=www.mydomain.com, OU=Infrastructure Team, DC=mydomain, DC=com', - default_field: false, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Numeric part of the version parsed from the original string.', - example: '1.2', - default_field: false, - }, - { - name: 'version_protocol', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Normalized lowercase protocol name parsed from original string.', - example: 'tls', - default_field: false, - }, - ], - }, - { - name: 'tracing', - title: 'Tracing', - group: 2, - description: - 'Distributed tracing makes it possible to analyze performance throughout\na microservice architecture all in one view. This is accomplished by tracing\nall of the requests - from the initial web request in the front-end service\n- to queries made through multiple back-end services.', - type: 'group', - fields: [ - { - name: 'trace.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of the trace.\n\nA trace groups multiple events like transactions that belong together. For\nexample, a user request handled by multiple inter-connected services.', - example: '4bf92f3577b34da6a3ce929d0e0e4736', - }, - { - name: 'transaction.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique identifier of the transaction.\n\nA transaction is the highest level of work measured within a service, such\nas a request to a server.', - example: '00f067aa0ba902b7', - }, - ], - }, - { - name: 'url', - title: 'URL', - group: 2, - description: - 'URL fields provide support for complete or partial URLs, and supports\nthe breaking down into scheme, domain, path, and so on.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Domain of the url, such as "www.elastic.co".\n\nIn some cases a URL may refer to an IP and/or port directly, without a domain\nname. In this case, the IP address would go to the `domain` field.', - example: 'www.elastic.co', - }, - { - name: 'extension', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The field contains the file extension from the original request\nurl.\n\nThe file extension is only set if it exists, as not every url has a file extension.\n\nThe leading period must not be included. For example, the value must be "png",\nnot ".png".', - example: 'png', - }, - { - name: 'fragment', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Portion of the url after the `#`, such as "top".\n\nThe `#` is not part of the fragment.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'If full URLs are important to your use case, they should be stored\nin `url.full`, whether this field is reconstructed or present in the event\nsource.', - example: 'https://www.elastic.co:443/search?q=elasticsearch#top', - }, - { - name: 'original', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: - 'Unmodified original url as seen in the event source.\n\nNote that in network monitoring, the observed URL may be a full URL, whereas\nin access logs, the URL is often just represented as a path.\n\nThis field is meant to represent the URL as it was observed, complete or not.', - example: - 'https://www.elastic.co:443/search?q=elasticsearch#top or /search?q=elasticsearch', - }, - { - name: 'password', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Password of the request.', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Path of the request, such as "/search".', - }, - { - name: 'port', - level: 'extended', - type: 'long', - format: 'string', - description: 'Port of the request, such as 443.', - example: 443, - }, - { - name: 'query', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The query field describes the query string of the request, such\nas "q=elasticsearch".\n\nThe `?` is excluded from the query string. If a URL contains no `?`, there\nis no query field. If there is a `?` but no query, the query field exists\nwith an empty string. The `exists` query can be used to differentiate between\nthe two cases.', - }, - { - name: 'registered_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The highest registered url domain, stripped of the subdomain.\n\nFor example, the registered domain for "foo.google.com" is "google.com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last two labels will not work well for TLDs such as "co.uk".', - example: 'google.com', - }, - { - name: 'scheme', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Scheme of the request, such as "https".\n\nNote: The `:` is not part of the scheme.', - example: 'https', - }, - { - name: 'top_level_domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The effective top level domain (eTLD), also known as the domain\nsuffix, is the last part of the domain name. For example, the top level domain\nfor google.com is "com".\n\nThis value can be determined precisely with a list like the public suffix\nlist (http://publicsuffix.org). Trying to approximate this by simply taking\nthe last label will not work well for effective TLDs such as "co.uk".', - example: 'co.uk', - }, - { - name: 'username', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Username of the request.', - }, - ], - }, - { - name: 'user', - title: 'User', - group: 2, - description: - 'The user fields describe information about the user that is relevant\nto the event.\n\nFields can have one entry or multiple entries. If a user has more than one id,\nprovide an array that includes all of them.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'email', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'User email address.', - }, - { - name: 'full_name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'group.domain', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', - }, - { - name: 'group.id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'group.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the group.', - }, - { - name: 'hash', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - ignore_above: 1024, - description: 'Unique identifiers of the user.', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'user_agent', - title: 'User agent', - group: 2, - description: - 'The user_agent fields normally come from a browser request.\n\nThey often show up in web service logs coming from the parsed user agent string.', - type: 'group', - fields: [ - { - name: 'device.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the device.', - example: 'iPhone', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Name of the user agent.', - example: 'Safari', - }, - { - name: 'original', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: 'Unparsed user_agent string.', - example: - 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15\n(KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - default_field: false, - }, - ], - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Version of the user agent.', - example: 12, - }, - ], - }, - { - name: 'vlan', - title: 'VLAN', - group: 2, - description: - 'The VLAN fields are used to identify 802.1q tag(s) of a packet,\nas well as ingress and egress VLAN associations of an observer in relation to\na specific packet or connection.\n\nNetwork.vlan fields are used to record a single VLAN tag, or the outer tag in\nthe case of q-in-q encapsulations, for a packet or connection as observed, typically\nprovided by a network sensor (e.g. Zeek, Wireshark) passively reporting on traffic.\n\nNetwork.inner VLAN fields are used to report inner q-in-q 802.1q tags (multiple\n802.1q encapsulations) as observed, typically provided by a network sensor (e.g.\nZeek, Wireshark) passively reporting on traffic. Network.inner VLAN fields should\nonly be used in addition to network.vlan fields to indicate q-in-q tagging.\n\nObserver.ingress and observer.egress VLAN values are used to record observer\nspecific information when observer events contain discrete ingress and egress\nVLAN information, typically provided by firewalls, routers, or load balancers.', - type: 'group', - fields: [ - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'VLAN ID as reported by the observer.', - example: 10, - default_field: false, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'Optional VLAN name as reported by the observer.', - example: 'outside', - default_field: false, - }, - ], - }, - { - name: 'vulnerability', - title: 'Vulnerability', - group: 2, - description: - 'The vulnerability fields describe information about a vulnerability\nthat is relevant to an event.', - type: 'group', - fields: [ - { - name: 'category', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of system or architecture that the vulnerability affects.\nThese may be platform-specific (for example, Debian or SUSE) or general (for\nexample, Database or Firewall). For example (https://qualysguard.qualys.com/qwebhelp/fo_portal/knowledgebase/vulnerability_categories.htm[Qualys\nvulnerability categories])\n\nThis field must be an array.', - example: '["Firewall"]', - default_field: false, - }, - { - name: 'classification', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The classification of the vulnerability scoring system. For example\n(https://www.first.org/cvss/)', - example: 'CVSS', - default_field: false, - }, - { - name: 'description', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - multi_fields: [ - { - name: 'text', - type: 'text', - norms: false, - }, - ], - description: - 'The description of the vulnerability that provides additional context\nof the vulnerability. For example (https://cve.mitre.org/about/faqs.html#cve_entry_descriptions_created[Common\nVulnerabilities and Exposure CVE description])', - example: 'In macOS before 2.12.6, there is a vulnerability in the RPC...', - default_field: false, - }, - { - name: 'enumeration', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The type of identifier used for this vulnerability. For example\n(https://cve.mitre.org/about/)', - example: 'CVE', - default_field: false, - }, - { - name: 'id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The identification (ID) is the number portion of a vulnerability\nentry. It includes a unique identification number for the vulnerability. For\nexample (https://cve.mitre.org/about/faqs.html#what_is_cve_id)[Common Vulnerabilities\nand Exposure CVE ID]', - example: 'CVE-2019-00001', - default_field: false, - }, - { - name: 'reference', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'A resource that provides additional information, context, and mitigations\nfor the identified vulnerability.', - example: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6111', - default_field: false, - }, - { - name: 'report_id', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The report or scan identification number.', - example: 20191018.0001, - default_field: false, - }, - { - name: 'scanner.vendor', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: 'The name of the vulnerability scanner vendor.', - example: 'Tenable', - default_field: false, - }, - { - name: 'score.base', - level: 'extended', - type: 'float', - description: - 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe.\n\nBase scores cover an assessment for exploitability metrics (attack vector,\ncomplexity, privileges, and user interaction), impact metrics (confidentiality,\nintegrity, and availability), and scope. For example (https://www.first.org/cvss/specification-document)', - example: 5.5, - default_field: false, - }, - { - name: 'score.environmental', - level: 'extended', - type: 'float', - description: - 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe.\n\nEnvironmental scores cover an assessment for any modified Base metrics, confidentiality,\nintegrity, and availability requirements. For example (https://www.first.org/cvss/specification-document)', - example: 5.5, - default_field: false, - }, - { - name: 'score.temporal', - level: 'extended', - type: 'float', - description: - 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe.\n\nTemporal scores cover an assessment for code maturity, remediation level,\nand confidence. For example (https://www.first.org/cvss/specification-document)', - default_field: false, - }, - { - name: 'score.version', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The National Vulnerability Database (NVD) provides qualitative\nseverity rankings of "Low", "Medium", and "High" for CVSS v2.0 base score\nranges in addition to the severity ratings for CVSS v3.0 as they are defined\nin the CVSS v3.0 specification.\n\nCVSS is owned and managed by FIRST.Org, Inc. (FIRST), a US-based non-profit\norganization, whose mission is to help computer security incident response\nteams across the world. For example (https://nvd.nist.gov/vuln-metrics/cvss)', - example: 2, - default_field: false, - }, - { - name: 'severity', - level: 'extended', - type: 'keyword', - ignore_above: 1024, - description: - 'The severity of the vulnerability can help with metrics and internal\nprioritization regarding remediation. For example (https://nvd.nist.gov/vuln-metrics/cvss)', - example: 'Critical', - default_field: false, - }, - ], - }, - ], - }, - { - key: 'beat', - anchor: 'beat-common', - title: 'Beat', - description: 'Contains common beat fields available in all event types.\n', - fields: [ - { - name: 'agent.hostname', - type: 'keyword', - description: 'Hostname of the agent.', - }, - { - name: 'beat.timezone', - type: 'alias', - path: 'event.timezone', - migration: true, - }, - { - name: 'fields', - type: 'object', - object_type: 'keyword', - description: 'Contains user configurable fields.\n', - }, - { - name: 'beat.name', - type: 'alias', - path: 'host.name', - migration: true, - }, - { - name: 'beat.hostname', - type: 'alias', - path: 'agent.hostname', - migration: true, - }, - { - name: 'timeseries.instance', - type: 'keyword', - description: 'Time series instance id', - }, - ], - }, - { - key: 'cloud', - title: 'Cloud provider metadata', - description: 'Metadata from cloud providers added by the add_cloud_metadata processor.\n', - fields: [ - { - name: 'cloud.project.id', - example: 'project-x', - description: 'Name of the project in Google Cloud.\n', - }, - { - name: 'cloud.image.id', - example: 'ami-abcd1234', - description: 'Image ID for the cloud instance.\n', - }, - { - name: 'meta.cloud.provider', - type: 'alias', - path: 'cloud.provider', - migration: true, - }, - { - name: 'meta.cloud.instance_id', - type: 'alias', - path: 'cloud.instance.id', - migration: true, - }, - { - name: 'meta.cloud.instance_name', - type: 'alias', - path: 'cloud.instance.name', - migration: true, - }, - { - name: 'meta.cloud.machine_type', - type: 'alias', - path: 'cloud.machine.type', - migration: true, - }, - { - name: 'meta.cloud.availability_zone', - type: 'alias', - path: 'cloud.availability_zone', - migration: true, - }, - { - name: 'meta.cloud.project_id', - type: 'alias', - path: 'cloud.project.id', - migration: true, - }, - { - name: 'meta.cloud.region', - type: 'alias', - path: 'cloud.region', - migration: true, - }, - ], - }, - { - key: 'docker', - title: 'Docker', - description: 'Docker stats collected from Docker.\n', - short_config: false, - anchor: 'docker-processor', - fields: [ - { - name: 'docker', - type: 'group', - fields: [ - { - name: 'container.id', - type: 'alias', - path: 'container.id', - migration: true, - }, - { - name: 'container.image', - type: 'alias', - path: 'container.image.name', - migration: true, - }, - { - name: 'container.name', - type: 'alias', - path: 'container.name', - migration: true, - }, - { - name: 'container.labels', - type: 'object', - object_type: 'keyword', - description: 'Image labels.\n', - }, - ], - }, - ], - }, - { - key: 'host', - title: 'Host', - description: 'Info collected for the host machine.\n', - anchor: 'host-processor', - fields: [ - { - name: 'host', - type: 'group', - fields: [ - { - name: 'containerized', - type: 'boolean', - description: 'If the host is a container.\n', - }, - { - name: 'os.build', - type: 'keyword', - example: '18D109', - description: 'OS build information.\n', - }, - { - name: 'os.codename', - type: 'keyword', - example: 'stretch', - description: 'OS codename, if any.\n', - }, - ], - }, - ], - }, - { - key: 'kubernetes', - title: 'Kubernetes', - description: 'Kubernetes metadata added by the kubernetes processor\n', - short_config: false, - anchor: 'kubernetes-processor', - fields: [ - { - name: 'kubernetes', - type: 'group', - fields: [ - { - name: 'pod.name', - type: 'keyword', - description: 'Kubernetes pod name\n', - }, - { - name: 'pod.uid', - type: 'keyword', - description: 'Kubernetes Pod UID\n', - }, - { - name: 'namespace', - type: 'keyword', - description: 'Kubernetes namespace\n', - }, - { - name: 'node.name', - type: 'keyword', - description: 'Kubernetes node name\n', - }, - { - name: 'labels.*', - type: 'object', - object_type: 'keyword', - object_type_mapping_type: '*', - description: 'Kubernetes labels map\n', - }, - { - name: 'annotations.*', - type: 'object', - object_type: 'keyword', - object_type_mapping_type: '*', - description: 'Kubernetes annotations map\n', - }, - { - name: 'replicaset.name', - type: 'keyword', - description: 'Kubernetes replicaset name\n', - }, - { - name: 'deployment.name', - type: 'keyword', - description: 'Kubernetes deployment name\n', - }, - { - name: 'statefulset.name', - type: 'keyword', - description: 'Kubernetes statefulset name\n', - }, - { - name: 'container.name', - type: 'keyword', - description: 'Kubernetes container name\n', - }, - { - name: 'container.image', - type: 'keyword', - description: 'Kubernetes container image\n', - }, - ], - }, - ], - }, - { - key: 'process', - title: 'Process', - description: 'Process metadata fields\n', - fields: [ - { - name: 'process', - type: 'group', - fields: [ - { - name: 'exe', - type: 'alias', - path: 'process.executable', - migration: true, - }, - ], - }, - ], - }, - { - key: 'jolokia-autodiscover', - title: 'Jolokia Discovery autodiscover provider', - description: 'Metadata from Jolokia Discovery added by the jolokia provider.\n', - fields: [ - { - name: 'jolokia.agent.version', - type: 'keyword', - description: 'Version number of jolokia agent.\n', - }, - { - name: 'jolokia.agent.id', - type: 'keyword', - description: - 'Each agent has a unique id which can be either provided during startup of the agent in form of a configuration parameter or being autodetected. If autodected, the id has several parts: The IP, the process id, hashcode of the agent and its type.\n', - }, - { - name: 'jolokia.server.product', - type: 'keyword', - description: 'The container product if detected.\n', - }, - { - name: 'jolokia.server.version', - type: 'keyword', - description: "The container's version (if detected).\n", - }, - { - name: 'jolokia.server.vendor', - type: 'keyword', - description: 'The vendor of the container the agent is running in.\n', - }, - { - name: 'jolokia.url', - type: 'keyword', - description: 'The URL how this agent can be contacted.\n', - }, - { - name: 'jolokia.secured', - type: 'boolean', - description: 'Whether the agent was configured for authentication or not.\n', - }, - ], - }, - { - key: 'common', - title: 'Common', - description: - 'These fields contain data about the environment in which the transaction or flow was captured.\n', - fields: [ - { - name: 'type', - description: - 'The type of the transaction (for example, HTTP, MySQL, Redis, or RUM) or "flow" in case of flows.\n', - required: true, - }, - { - name: 'server.process.name', - description: 'The name of the process that served the transaction.\n', - }, - { - name: 'server.process.args', - description: 'The command-line of the process that served the transaction.\n', - }, - { - name: 'server.process.executable', - description: 'Absolute path to the server process executable.\n', - }, - { - name: 'server.process.working_directory', - description: 'The working directory of the server process.\n', - }, - { - name: 'server.process.start', - description: 'The time the server process started.\n', - }, - { - name: 'client.process.name', - description: 'The name of the process that initiated the transaction.\n', - }, - { - name: 'client.process.args', - description: 'The command-line of the process that initiated the transaction.\n', - }, - { - name: 'client.process.executable', - description: 'Absolute path to the client process executable.\n', - }, - { - name: 'client.process.working_directory', - description: 'The working directory of the client process.\n', - }, - { - name: 'client.process.start', - description: 'The time the client process started.\n', - }, - { - name: 'real_ip', - type: 'alias', - path: 'network.forwarded_ip', - migration: true, - description: - 'If the server initiating the transaction is a proxy, this field contains the original client IP address. For HTTP, for example, the IP address extracted from a configurable HTTP header, by default `X-Forwarded-For`.\nUnless this field is disabled, it always has a value, and it matches the `client_ip` for non proxy clients.\n', - }, - { - name: 'transport', - type: 'alias', - path: 'network.transport', - migration: true, - description: - 'The transport protocol used for the transaction. If not specified, then tcp is assumed.\n', - }, - ], - }, - { - key: 'flows_event', - title: 'Flow Event', - description: 'These fields contain data about the flow itself.\n', - fields: [ - { - name: 'flow.final', - type: 'boolean', - description: - 'Indicates if event is last event in flow. If final is false, the event reports an intermediate flow state only.\n', - }, - { - name: 'flow.id', - description: 'Internal flow ID based on connection meta data and address.\n', - }, - { - name: 'flow.vlan', - type: 'long', - description: - "VLAN identifier from the 802.1q frame. In case of a multi-tagged frame this field will be an array with the outer tag's VLAN identifier listed first.\n", - }, - { - name: 'flow_id', - type: 'alias', - path: 'flow.id', - migration: true, - }, - { - name: 'final', - type: 'alias', - path: 'flow.final', - migration: true, - }, - { - name: 'vlan', - type: 'alias', - path: 'flow.vlan', - migration: true, - }, - { - name: 'source.stats.net_bytes_total', - type: 'alias', - path: 'source.bytes', - migration: true, - }, - { - name: 'source.stats.net_packets_total', - type: 'alias', - path: 'source.packets', - migration: true, - }, - { - name: 'dest.stats.net_bytes_total', - type: 'alias', - path: 'destination.bytes', - migration: true, - }, - { - name: 'dest.stats.net_packets_total', - type: 'alias', - path: 'destination.packets', - migration: true, - }, - ], - }, - { - key: 'trans_event', - title: 'Transaction Event', - description: 'These fields contain data about the transaction itself.\n', - fields: [ - { - name: 'status', - description: - 'The high level status of the transaction. The way to compute this value depends on the protocol, but the result has a meaning independent of the protocol.\n', - required: true, - possible_values: ['OK', 'Error', 'Server Error', 'Client Error'], - }, - { - name: 'method', - description: - 'The command/verb/method of the transaction. For HTTP, this is the method name (GET, POST, PUT, and so on), for SQL this is the verb (SELECT, UPDATE, DELETE, and so on).\n', - }, - { - name: 'resource', - description: - 'The logical resource that this transaction refers to. For HTTP, this is the URL path up to the last slash (/). For example, if the URL is `/users/1`, the resource is `/users`. For databases, the resource is typically the table name. The field is not filled for all transaction types.\n', - }, - { - name: 'path', - required: true, - description: - 'The path the transaction refers to. For HTTP, this is the URL. For SQL databases, this is the table name. For key-value stores, this is the key.\n', - }, - { - name: 'query', - type: 'keyword', - description: - 'The query in a human readable format. For HTTP, it will typically be something like `GET /users/_search?name=test`. For MySQL, it is something like `SELECT id from users where name=test`.\n', - }, - { - name: 'params', - type: 'text', - description: - 'The request parameters. For HTTP, these are the POST or GET parameters. For Thrift-RPC, these are the parameters from the request.\n', - }, - { - name: 'notes', - type: 'alias', - path: 'error.message', - description: - 'Messages from Packetbeat itself. This field usually contains error messages for interpreting the raw data. This information can be helpful for troubleshooting.\n', - }, - ], - }, - { - key: 'raw', - title: 'Raw', - description: 'These fields contain the raw transaction data.', - fields: [ - { - name: 'request', - type: 'text', - description: - 'For text protocols, this is the request as seen on the wire (application layer only). For binary protocols this is our representation of the request.\n', - }, - { - name: 'response', - type: 'text', - description: - 'For text protocols, this is the response as seen on the wire (application layer only). For binary protocols this is our representation of the request.\n', - }, - ], - }, - { - key: 'trans_measurements', - title: 'Measurements (Transactions)', - description: 'These fields contain measurements related to the transaction.\n', - fields: [ - { - name: 'bytes_in', - type: 'alias', - path: 'source.bytes', - description: - 'The number of bytes of the request. Note that this size is the application layer message length, without the length of the IP or TCP headers.\n', - }, - { - name: 'bytes_out', - type: 'alias', - path: 'destination.bytes', - description: - 'The number of bytes of the response. Note that this size is the application layer message length, without the length of the IP or TCP headers.\n', - }, - ], - }, - { - key: 'amqp', - title: 'AMQP', - description: 'AMQP specific event fields.', - fields: [ - { - name: 'amqp', - type: 'group', - fields: [ - { - name: 'reply-code', - type: 'long', - description: 'AMQP reply code to an error, similar to http reply-code\n', - example: 404, - }, - { - name: 'reply-text', - type: 'keyword', - description: 'Text explaining the error.\n', - }, - { - name: 'class-id', - type: 'long', - description: 'Failing method class.\n', - }, - { - name: 'method-id', - type: 'long', - description: 'Failing method ID.\n', - }, - { - name: 'exchange', - type: 'keyword', - description: 'Name of the exchange.\n', - }, - { - name: 'exchange-type', - type: 'keyword', - description: 'Exchange type.\n', - example: 'fanout', - }, - { - name: 'passive', - type: 'boolean', - description: 'If set, do not create exchange/queue.\n', - }, - { - name: 'durable', - type: 'boolean', - description: 'If set, request a durable exchange/queue.\n', - }, - { - name: 'exclusive', - type: 'boolean', - description: 'If set, request an exclusive queue.\n', - }, - { - name: 'auto-delete', - type: 'boolean', - description: 'If set, auto-delete queue when unused.\n', - }, - { - name: 'no-wait', - type: 'boolean', - description: 'If set, the server will not respond to the method.\n', - }, - { - name: 'consumer-tag', - description: 'Identifier for the consumer, valid within the current channel.\n', - }, - { - name: 'delivery-tag', - type: 'long', - description: 'The server-assigned and channel-specific delivery tag.\n', - }, - { - name: 'message-count', - type: 'long', - description: - 'The number of messages in the queue, which will be zero for newly-declared queues.\n', - }, - { - name: 'consumer-count', - type: 'long', - description: 'The number of consumers of a queue.\n', - }, - { - name: 'routing-key', - type: 'keyword', - description: 'Message routing key.\n', - }, - { - name: 'no-ack', - type: 'boolean', - description: 'If set, the server does not expect acknowledgements for messages.\n', - }, - { - name: 'no-local', - type: 'boolean', - description: - 'If set, the server will not send messages to the connection that published them.\n', - }, - { - name: 'if-unused', - type: 'boolean', - description: 'Delete only if unused.\n', - }, - { - name: 'if-empty', - type: 'boolean', - description: 'Delete only if empty.\n', - }, - { - name: 'queue', - type: 'keyword', - description: 'The queue name identifies the queue within the vhost.\n', - }, - { - name: 'redelivered', - type: 'boolean', - description: - 'Indicates that the message has been previously delivered to this or another client.\n', - }, - { - name: 'multiple', - type: 'boolean', - description: 'Acknowledge multiple messages.\n', - }, - { - name: 'arguments', - type: 'object', - description: - 'Optional additional arguments passed to some methods. Can be of various types.\n', - }, - { - name: 'mandatory', - type: 'boolean', - description: 'Indicates mandatory routing.\n', - }, - { - name: 'immediate', - type: 'boolean', - description: 'Request immediate delivery.\n', - }, - { - name: 'content-type', - type: 'keyword', - description: 'MIME content type.\n', - example: 'text/plain', - }, - { - name: 'content-encoding', - type: 'keyword', - description: 'MIME content encoding.\n', - }, - { - name: 'headers', - type: 'object', - object_type: 'keyword', - description: 'Message header field table.\n', - }, - { - name: 'delivery-mode', - type: 'keyword', - description: 'Non-persistent (1) or persistent (2).\n', - }, - { - name: 'priority', - type: 'long', - description: 'Message priority, 0 to 9.\n', - }, - { - name: 'correlation-id', - type: 'keyword', - description: 'Application correlation identifier.\n', - }, - { - name: 'reply-to', - type: 'keyword', - description: 'Address to reply to.\n', - }, - { - name: 'expiration', - type: 'keyword', - description: 'Message expiration specification.\n', - }, - { - name: 'message-id', - type: 'keyword', - description: 'Application message identifier.\n', - }, - { - name: 'timestamp', - type: 'keyword', - description: 'Message timestamp.\n', - }, - { - name: 'type', - type: 'keyword', - description: 'Message type name.\n', - }, - { - name: 'user-id', - type: 'keyword', - description: 'Creating user id.\n', - }, - { - name: 'app-id', - type: 'keyword', - description: 'Creating application id.\n', - }, - ], - }, - ], - }, - { - key: 'cassandra', - title: 'Cassandra', - description: 'Cassandra v4/3 specific event fields.', - fields: [ - { - name: 'no_request', - type: 'alias', - path: 'cassandra.no_request', - migration: true, - }, - { - name: 'cassandra', - type: 'group', - description: 'Information about the Cassandra request and response.', - fields: [ - { - name: 'no_request', - type: 'boolean', - description: 'Indicates that there is no request because this is a PUSH message.\n', - }, - { - name: 'request', - type: 'group', - description: 'Cassandra request.', - fields: [ - { - name: 'headers', - type: 'group', - description: 'Cassandra request headers.', - fields: [ - { - name: 'version', - type: 'long', - description: 'The version of the protocol.', - }, - { - name: 'flags', - type: 'keyword', - description: 'Flags applying to this frame.', - }, - { - name: 'stream', - type: 'keyword', - description: - 'A frame has a stream id. If a client sends a request message with the stream id X, it is guaranteed that the stream id of the response to that message will be X.', - }, - { - name: 'op', - type: 'keyword', - description: 'An operation type that distinguishes the actual message.', - }, - { - name: 'length', - type: 'long', - description: - 'A integer representing the length of the body of the frame (a frame is limited to 256MB in length).', - }, - ], - }, - { - name: 'query', - type: 'keyword', - description: 'The CQL query which client send to cassandra.', - }, - ], - }, - { - name: 'response', - type: 'group', - description: 'Cassandra response.', - fields: [ - { - name: 'headers', - type: 'group', - description: - "Cassandra response headers, the structure is as same as request's header.", - fields: [ - { - name: 'version', - type: 'long', - description: 'The version of the protocol.', - }, - { - name: 'flags', - type: 'keyword', - description: 'Flags applying to this frame.', - }, - { - name: 'stream', - type: 'keyword', - description: - 'A frame has a stream id. If a client sends a request message with the stream id X, it is guaranteed that the stream id of the response to that message will be X.', - }, - { - name: 'op', - type: 'keyword', - description: 'An operation type that distinguishes the actual message.', - }, - { - name: 'length', - type: 'long', - description: - 'A integer representing the length of the body of the frame (a frame is limited to 256MB in length).', - }, - ], - }, - { - name: 'result', - type: 'group', - description: 'Details about the returned result.', - fields: [ - { - name: 'type', - type: 'keyword', - description: 'Cassandra result type.', - }, - { - name: 'rows', - type: 'group', - description: 'Details about the rows.', - fields: [ - { - name: 'num_rows', - type: 'long', - description: 'Representing the number of rows present in this result.', - }, - { - name: 'meta', - type: 'group', - description: 'Composed of result metadata.', - fields: [ - { - name: 'keyspace', - type: 'keyword', - description: - 'Only present after set Global_tables_spec, the keyspace name.', - }, - { - name: 'table', - type: 'keyword', - description: - 'Only present after set Global_tables_spec, the table name.', - }, - { - name: 'flags', - type: 'keyword', - description: - 'Provides information on the formatting of the remaining information.', - }, - { - name: 'col_count', - type: 'long', - description: - 'Representing the number of columns selected by the query that produced this result.', - }, - { - name: 'pkey_columns', - type: 'long', - description: 'Representing the PK columns index and counts.', - }, - { - name: 'paging_state', - type: 'keyword', - description: - 'The paging_state is a bytes value that should be used in QUERY/EXECUTE to continue paging and retrieve the remainder of the result for this query.', - }, - ], - }, - ], - }, - { - name: 'keyspace', - type: 'keyword', - description: 'Indicating the name of the keyspace that has been set.', - }, - { - name: 'schema_change', - type: 'group', - description: 'The result to a schema_change message.', - fields: [ - { - name: 'change', - type: 'keyword', - description: 'Representing the type of changed involved.', - }, - { - name: 'keyspace', - type: 'keyword', - description: 'This describes which keyspace has changed.', - }, - { - name: 'table', - type: 'keyword', - description: 'This describes which table has changed.', - }, - { - name: 'object', - type: 'keyword', - description: - 'This describes the name of said affected object (either the table, user type, function, or aggregate name).', - }, - { - name: 'target', - type: 'keyword', - description: - 'Target could be "FUNCTION" or "AGGREGATE", multiple arguments.', - }, - { - name: 'name', - type: 'keyword', - description: 'The function/aggregate name.', - }, - { - name: 'args', - type: 'keyword', - description: 'One string for each argument type (as CQL type).', - }, - ], - }, - { - name: 'prepared', - type: 'group', - description: 'The result to a PREPARE message.', - fields: [ - { - name: 'prepared_id', - type: 'keyword', - description: 'Representing the prepared query ID.', - }, - { - name: 'req_meta', - type: 'group', - description: 'This describes the request metadata.', - fields: [ - { - name: 'keyspace', - type: 'keyword', - description: - 'Only present after set Global_tables_spec, the keyspace name.', - }, - { - name: 'table', - type: 'keyword', - description: - 'Only present after set Global_tables_spec, the table name.', - }, - { - name: 'flags', - type: 'keyword', - description: - 'Provides information on the formatting of the remaining information.', - }, - { - name: 'col_count', - type: 'long', - description: - 'Representing the number of columns selected by the query that produced this result.', - }, - { - name: 'pkey_columns', - type: 'long', - description: 'Representing the PK columns index and counts.', - }, - { - name: 'paging_state', - type: 'keyword', - description: - 'The paging_state is a bytes value that should be used in QUERY/EXECUTE to continue paging and retrieve the remainder of the result for this query.', - }, - ], - }, - { - name: 'resp_meta', - type: 'group', - description: 'This describes the metadata for the result set.', - fields: [ - { - name: 'keyspace', - type: 'keyword', - description: - 'Only present after set Global_tables_spec, the keyspace name.', - }, - { - name: 'table', - type: 'keyword', - description: - 'Only present after set Global_tables_spec, the table name.', - }, - { - name: 'flags', - type: 'keyword', - description: - 'Provides information on the formatting of the remaining information.', - }, - { - name: 'col_count', - type: 'long', - description: - 'Representing the number of columns selected by the query that produced this result.', - }, - { - name: 'pkey_columns', - type: 'long', - description: 'Representing the PK columns index and counts.', - }, - { - name: 'paging_state', - type: 'keyword', - description: - 'The paging_state is a bytes value that should be used in QUERY/EXECUTE to continue paging and retrieve the remainder of the result for this query.', - }, - ], - }, - ], - }, - ], - }, - { - name: 'supported', - type: 'object', - object_type: 'keyword', - description: - 'Indicates which startup options are supported by the server. This message comes as a response to an OPTIONS message.', - }, - { - name: 'authentication', - type: 'group', - description: - 'Indicates that the server requires authentication, and which authentication mechanism to use.', - fields: [ - { - name: 'class', - type: 'keyword', - description: 'Indicates the full class name of the IAuthenticator in use', - }, - ], - }, - { - name: 'warnings', - type: 'keyword', - description: 'The text of the warnings, only occur when Warning flag was set.', - }, - { - name: 'event', - type: 'group', - description: - 'Event pushed by the server. A client will only receive events for the types it has REGISTERed to.', - fields: [ - { - name: 'type', - type: 'keyword', - description: 'Representing the event type.', - }, - { - name: 'change', - type: 'keyword', - description: - 'The message corresponding respectively to the type of change followed by the address of the new/removed node.', - }, - { - name: 'host', - type: 'keyword', - description: 'Representing the node ip.', - }, - { - name: 'port', - type: 'long', - description: 'Representing the node port.', - }, - { - name: 'schema_change', - type: 'group', - description: 'The events details related to schema change.', - fields: [ - { - name: 'change', - type: 'keyword', - description: 'Representing the type of changed involved.', - }, - { - name: 'keyspace', - type: 'keyword', - description: 'This describes which keyspace has changed.', - }, - { - name: 'table', - type: 'keyword', - description: 'This describes which table has changed.', - }, - { - name: 'object', - type: 'keyword', - description: - 'This describes the name of said affected object (either the table, user type, function, or aggregate name).', - }, - { - name: 'target', - type: 'keyword', - description: - 'Target could be "FUNCTION" or "AGGREGATE", multiple arguments.', - }, - { - name: 'name', - type: 'keyword', - description: 'The function/aggregate name.', - }, - { - name: 'args', - type: 'keyword', - description: 'One string for each argument type (as CQL type).', - }, - ], - }, - ], - }, - { - name: 'error', - type: 'group', - description: - 'Indicates an error processing a request. The body of the message will be an error code followed by a error message. Then, depending on the exception, more content may follow.', - fields: [ - { - name: 'code', - type: 'long', - description: 'The error code of the Cassandra response.', - }, - { - name: 'msg', - type: 'keyword', - description: 'The error message of the Cassandra response.', - }, - { - name: 'type', - type: 'keyword', - description: 'The error type of the Cassandra response.', - }, - { - name: 'details', - type: 'group', - description: 'The details of the error.', - fields: [ - { - name: 'read_consistency', - type: 'keyword', - description: - 'Representing the consistency level of the query that triggered the exception.', - }, - { - name: 'required', - type: 'long', - description: - 'Representing the number of nodes that should be alive to respect consistency level.', - }, - { - name: 'alive', - type: 'long', - description: - 'Representing the number of replicas that were known to be alive when the request had been processed (since an unavailable exception has been triggered).', - }, - { - name: 'received', - type: 'long', - description: - 'Representing the number of nodes having acknowledged the request.', - }, - { - name: 'blockfor', - type: 'long', - description: - 'Representing the number of replicas whose acknowledgement is required to achieve consistency level.', - }, - { - name: 'write_type', - type: 'keyword', - description: 'Describe the type of the write that timed out.', - }, - { - name: 'data_present', - type: 'boolean', - description: 'It means the replica that was asked for data had responded.', - }, - { - name: 'keyspace', - type: 'keyword', - description: 'The keyspace of the failed function.', - }, - { - name: 'table', - type: 'keyword', - description: 'The keyspace of the failed function.', - }, - { - name: 'stmt_id', - type: 'keyword', - description: 'Representing the unknown ID.', - }, - { - name: 'num_failures', - type: 'keyword', - description: - 'Representing the number of nodes that experience a failure while executing the request.', - }, - { - name: 'function', - type: 'keyword', - description: 'The name of the failed function.', - }, - { - name: 'arg_types', - type: 'keyword', - description: - 'One string for each argument type (as CQL type) of the failed function.', - }, - ], - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - key: 'dhcpv4', - title: 'DHCPv4', - description: 'DHCPv4 event fields', - fields: [ - { - name: 'dhcpv4', - type: 'group', - fields: [ - { - name: 'transaction_id', - type: 'keyword', - description: - 'Transaction ID, a random number chosen by the\nclient, used by the client and server to associate\nmessages and responses between a client and a\nserver.\n', - }, - { - name: 'seconds', - type: 'long', - description: - 'Number of seconds elapsed since client began address acquisition or\nrenewal process.\n', - }, - { - name: 'flags', - type: 'keyword', - description: - 'Flags are set by the client to indicate how the DHCP server should\nits reply -- either unicast or broadcast.\n', - }, - { - name: 'client_ip', - type: 'ip', - description: 'The current IP address of the client.', - }, - { - name: 'assigned_ip', - type: 'ip', - description: - 'The IP address that the DHCP server is assigning to the client.\nThis field is also known as "your" IP address.\n', - }, - { - name: 'server_ip', - type: 'ip', - description: - 'The IP address of the DHCP server that the client should use for the\nnext step in the bootstrap process.\n', - }, - { - name: 'relay_ip', - type: 'ip', - description: - 'The relay IP address used by the client to contact the server\n(i.e. a DHCP relay server).\n', - }, - { - name: 'client_mac', - type: 'keyword', - description: "The client's MAC address (layer two).", - }, - { - name: 'server_name', - type: 'keyword', - description: - 'The name of the server sending the message. Optional. Used in\nDHCPOFFER or DHCPACK messages.\n', - }, - { - name: 'op_code', - type: 'keyword', - example: 'bootreply', - description: 'The message op code (bootrequest or bootreply).\n', - }, - { - name: 'hops', - type: 'long', - description: 'The number of hops the DHCP message went through.', - }, - { - name: 'hardware_type', - type: 'keyword', - description: - 'The type of hardware used for the local network (Ethernet,\nLocalTalk, etc).\n', - }, - { - name: 'option', - type: 'group', - fields: [ - { - name: 'message_type', - type: 'keyword', - example: 'ack', - description: - 'The specific type of DHCP message being sent (e.g. discover,\noffer, request, decline, ack, nak, release, inform).\n', - }, - { - name: 'parameter_request_list', - type: 'keyword', - description: - 'This option is used by a DHCP client to request values for\nspecified configuration parameters.\n', - }, - { - name: 'requested_ip_address', - type: 'ip', - description: - 'This option is used in a client request (DHCPDISCOVER) to allow\nthe client to request that a particular IP address be assigned.\n', - }, - { - name: 'server_identifier', - type: 'ip', - description: - 'IP address of the individual DHCP server which handled this\nmessage.\n', - }, - { - name: 'broadcast_address', - type: 'ip', - description: - "This option specifies the broadcast address in use on the\nclient's subnet.\n", - }, - { - name: 'max_dhcp_message_size', - type: 'long', - description: - 'This option specifies the maximum length DHCP message that the\nclient is willing to accept.\n', - }, - { - name: 'class_identifier', - type: 'keyword', - description: - "This option is used by DHCP clients to optionally identify the\nvendor type and configuration of a DHCP client. Vendors may\nchoose to define specific vendor class identifiers to convey\nparticular configuration or other identification information\nabout a client. For example, the identifier may encode the\nclient's hardware configuration.\n", - }, - { - name: 'domain_name', - type: 'keyword', - description: - 'This option specifies the domain name that client should use\nwhen resolving hostnames via the Domain Name System.\n', - }, - { - name: 'dns_servers', - type: 'ip', - description: - 'The domain name server option specifies a list of Domain Name\nSystem servers available to the client.\n', - }, - { - name: 'vendor_identifying_options', - type: 'object', - description: - 'A DHCP client may use this option to unambiguously identify the\nvendor that manufactured the hardware on which the client is\nrunning, the software in use, or an industry consortium to which\nthe vendor belongs. This field is described in RFC 3925.\n', - }, - { - name: 'subnet_mask', - type: 'ip', - description: - 'The subnet mask that the client should use on the currnet\nnetwork.\n', - }, - { - name: 'utc_time_offset_sec', - type: 'long', - description: - "The time offset field specifies the offset of the client's\nsubnet in seconds from Coordinated Universal Time (UTC).\n", - }, - { - name: 'router', - type: 'ip', - description: - "The router option specifies a list of IP addresses for routers\non the client's subnet.\n", - }, - { - name: 'time_servers', - type: 'ip', - description: - 'The time server option specifies a list of RFC 868 time servers\navailable to the client.\n', - }, - { - name: 'ntp_servers', - type: 'ip', - description: - 'This option specifies a list of IP addresses indicating NTP\nservers available to the client.\n', - }, - { - name: 'hostname', - type: 'keyword', - description: 'This option specifies the name of the client.\n', - }, - { - name: 'ip_address_lease_time_sec', - type: 'long', - description: - 'This option is used in a client request (DHCPDISCOVER or\nDHCPREQUEST) to allow the client to request a lease time for the\nIP address. In a server reply (DHCPOFFER), a DHCP server uses\nthis option to specify the lease time it is willing to offer.\n', - }, - { - name: 'message', - type: 'text', - description: - 'This option is used by a DHCP server to provide an error message\nto a DHCP client in a DHCPNAK message in the event of a failure.\nA client may use this option in a DHCPDECLINE message to\nindicate the why the client declined the offered parameters.\n', - }, - { - name: 'renewal_time_sec', - type: 'long', - description: - 'This option specifies the time interval from address assignment\nuntil the client transitions to the RENEWING state.\n', - }, - { - name: 'rebinding_time_sec', - type: 'long', - description: - 'This option specifies the time interval from address assignment\nuntil the client transitions to the REBINDING state.\n', - }, - { - name: 'boot_file_name', - type: 'keyword', - description: - "This option is used to identify a bootfile when the 'file' field\nin the DHCP header has been used for DHCP options.\n", - }, - ], - }, - ], - }, - ], - }, - { - key: 'dns', - title: 'DNS', - description: 'DNS-specific event fields.', - fields: [ - { - name: 'dns', - type: 'group', - fields: [ - { - name: 'flags.authoritative', - type: 'boolean', - description: - 'A DNS flag specifying that the responding server is an authority for the domain name used in the question.\n', - }, - { - name: 'flags.recursion_available', - type: 'boolean', - description: - 'A DNS flag specifying whether recursive query support is available in the name server.\n', - }, - { - name: 'flags.recursion_desired', - type: 'boolean', - description: - 'A DNS flag specifying that the client directs the server to pursue a query recursively. Recursive query support is optional.\n', - }, - { - name: 'flags.authentic_data', - type: 'boolean', - description: - 'A DNS flag specifying that the recursive server considers the response authentic.\n', - }, - { - name: 'flags.checking_disabled', - type: 'boolean', - description: - 'A DNS flag specifying that the client disables the server signature validation of the query.\n', - }, - { - name: 'flags.truncated_response', - type: 'boolean', - description: - 'A DNS flag specifying that only the first 512 bytes of the reply were returned.\n', - }, - { - name: 'question.etld_plus_one', - description: - 'The effective top-level domain (eTLD) plus one more label.\nFor example, the eTLD+1 for "foo.bar.golang.org." is "golang.org.".\nThe data for determining the eTLD comes from an embedded copy of the\ndata from http://publicsuffix.org.', - example: 'amazon.co.uk.', - }, - { - name: 'answers_count', - type: 'long', - description: 'The number of resource records contained in the `dns.answers` field.\n', - }, - { - name: 'authorities', - type: 'object', - description: - 'An array containing a dictionary for each authority section from the answer.\n', - }, - { - name: 'authorities_count', - type: 'long', - description: - 'The number of resource records contained in the `dns.authorities` field. The `dns.authorities` field may or may not be included depending on the configuration of Packetbeat.\n', - }, - { - name: 'authorities.name', - description: 'The domain name to which this resource record pertains.', - example: 'example.com.', - }, - { - name: 'authorities.type', - description: 'The type of data contained in this resource record.', - example: 'NS', - }, - { - name: 'authorities.class', - description: 'The class of DNS data contained in this resource record.', - example: 'IN', - }, - { - name: 'additionals', - type: 'object', - description: - 'An array containing a dictionary for each additional section from the answer.\n', - }, - { - name: 'additionals_count', - type: 'long', - description: - 'The number of resource records contained in the `dns.additionals` field. The `dns.additionals` field may or may not be included depending on the configuration of Packetbeat.\n', - }, - { - name: 'additionals.name', - description: 'The domain name to which this resource record pertains.', - example: 'example.com.', - }, - { - name: 'additionals.type', - description: 'The type of data contained in this resource record.', - example: 'NS', - }, - { - name: 'additionals.class', - description: 'The class of DNS data contained in this resource record.', - example: 'IN', - }, - { - name: 'additionals.ttl', - description: - 'The time interval in seconds that this resource record may be cached before it should be discarded. Zero values mean that the data should not be cached.\n', - type: 'long', - }, - { - name: 'additionals.data', - description: - 'The data describing the resource. The meaning of this data depends on the type and class of the resource record.\n', - }, - { - name: 'opt.version', - description: 'The EDNS version.', - example: '0', - }, - { - name: 'opt.do', - type: 'boolean', - description: 'If set, the transaction uses DNSSEC.', - }, - { - name: 'opt.ext_rcode', - description: 'Extended response code field.', - example: 'BADVERS', - }, - { - name: 'opt.udp_size', - type: 'long', - description: "Requestor's UDP payload size (in bytes).", - }, - ], - }, - ], - }, - { - key: 'http', - title: 'HTTP', - description: 'HTTP-specific event fields.', - fields: [ - { - name: 'http', - type: 'group', - description: 'Information about the HTTP request and response.', - fields: [ - { - name: 'request', - description: 'HTTP request', - type: 'group', - fields: [ - { - name: 'headers', - type: 'object', - object_type: 'keyword', - description: - 'A map containing the captured header fields from the request. Which headers to capture is configurable. If headers with the same header name are present in the message, they will be separated by commas.\n', - }, - { - name: 'params', - type: 'alias', - migration: true, - path: 'url.query', - }, - ], - }, - { - name: 'response', - description: 'HTTP response', - type: 'group', - fields: [ - { - name: 'status_phrase', - description: 'The HTTP status phrase.', - example: 'Not Found', - }, - { - name: 'headers', - type: 'object', - object_type: 'keyword', - description: - 'A map containing the captured header fields from the response. Which headers to capture is configurable. If headers with the same header name are present in the message, they will be separated by commas.\n', - }, - { - name: 'code', - type: 'alias', - migration: true, - path: 'http.response.status_code', - }, - { - name: 'phrase', - type: 'alias', - migration: true, - path: 'http.response.status_phrase', - }, - ], - }, - ], - }, - ], - }, - { - key: 'icmp', - title: 'ICMP', - description: 'ICMP specific event fields.\n', - fields: [ - { - name: 'icmp', - type: 'group', - fields: [ - { - name: 'version', - description: 'The version of the ICMP protocol.', - possible_values: [4, 6], - }, - { - name: 'request.message', - type: 'keyword', - description: 'A human readable form of the request.', - }, - { - name: 'request.type', - type: 'long', - description: 'The request type.', - }, - { - name: 'request.code', - type: 'long', - description: 'The request code.', - }, - { - name: 'response.message', - type: 'keyword', - description: 'A human readable form of the response.', - }, - { - name: 'response.type', - type: 'long', - description: 'The response type.', - }, - { - name: 'response.code', - type: 'long', - description: 'The response code.', - }, - ], - }, - ], - }, - { - key: 'memcache', - title: 'Memcache', - description: 'Memcached-specific event fields', - fields: [ - { - name: 'memcache', - type: 'group', - fields: [ - { - name: 'protocol_type', - type: 'keyword', - description: - 'The memcache protocol implementation. The value can be "binary" for binary-based, "text" for text-based, or "unknown" for an unknown memcache protocol type.\n', - }, - { - name: 'request.line', - type: 'keyword', - description: 'The raw command line for unknown commands ONLY.\n', - }, - { - name: 'request.command', - type: 'keyword', - description: - 'The memcache command being requested in the memcache text protocol. For example "set" or "get". The binary protocol opcodes are translated into memcache text protocol commands.\n', - }, - { - name: 'response.command', - type: 'keyword', - description: - 'Either the text based protocol response message type or the name of the originating request if binary protocol is used.\n', - }, - { - name: 'request.type', - type: 'keyword', - description: - 'The memcache command classification. This value can be "UNKNOWN", "Load", "Store", "Delete", "Counter", "Info", "SlabCtrl", "LRUCrawler", "Stats", "Success", "Fail", or "Auth".\n', - }, - { - name: 'response.type', - type: 'keyword', - description: - 'The memcache command classification. This value can be "UNKNOWN", "Load", "Store", "Delete", "Counter", "Info", "SlabCtrl", "LRUCrawler", "Stats", "Success", "Fail", or "Auth". The text based protocol will employ any of these, whereas the binary based protocol will mirror the request commands only (see `memcache.response.status` for binary protocol).\n', - }, - { - name: 'response.error_msg', - type: 'keyword', - description: - 'The optional error message in the memcache response (text based protocol only).\n', - }, - { - name: 'request.opcode', - type: 'keyword', - description: 'The binary protocol message opcode name.\n', - }, - { - name: 'response.opcode', - type: 'keyword', - description: 'The binary protocol message opcode name.\n', - }, - { - name: 'request.opcode_value', - type: 'long', - description: 'The binary protocol message opcode value.\n', - }, - { - name: 'response.opcode_value', - type: 'long', - description: 'The binary protocol message opcode value.\n', - }, - { - name: 'request.opaque', - type: 'long', - description: - 'The binary protocol opaque header value used for correlating request with response messages.\n', - }, - { - name: 'response.opaque', - type: 'long', - description: - 'The binary protocol opaque header value used for correlating request with response messages.\n', - }, - { - name: 'request.vbucket', - type: 'long', - description: 'The vbucket index sent in the binary message.\n', - }, - { - name: 'response.status', - type: 'keyword', - description: - 'The textual representation of the response error code (binary protocol only).\n', - }, - { - name: 'response.status_code', - type: 'long', - description: 'The status code value returned in the response (binary protocol only).\n', - }, - { - name: 'request.keys', - type: 'array', - description: 'The list of keys sent in the store or load commands.\n', - }, - { - name: 'response.keys', - type: 'array', - description: 'The list of keys returned for the load command (if present).\n', - }, - { - name: 'request.count_values', - type: 'long', - description: - 'The number of values found in the memcache request message. If the command does not send any data, this field is missing.\n', - }, - { - name: 'response.count_values', - type: 'long', - description: - 'The number of values found in the memcache response message. If the command does not send any data, this field is missing.\n', - }, - { - name: 'request.values', - type: 'array', - description: 'The list of base64 encoded values sent with the request (if present).\n', - }, - { - name: 'response.values', - type: 'array', - description: 'The list of base64 encoded values sent with the response (if present).\n', - }, - { - name: 'request.bytes', - type: 'long', - format: 'bytes', - description: 'The byte count of the values being transferred.\n', - }, - { - name: 'response.bytes', - type: 'long', - format: 'bytes', - description: 'The byte count of the values being transferred.\n', - }, - { - name: 'request.delta', - type: 'long', - description: 'The counter increment/decrement delta value.\n', - }, - { - name: 'request.initial', - type: 'long', - description: - 'The counter increment/decrement initial value parameter (binary protocol only).\n', - }, - { - name: 'request.verbosity', - type: 'long', - description: 'The value of the memcache "verbosity" command.\n', - }, - { - name: 'request.raw_args', - type: 'keyword', - description: - 'The text protocol raw arguments for the "stats ..." and "lru crawl ..." commands.\n', - }, - { - name: 'request.source_class', - type: 'long', - description: "The source class id in 'slab reassign' command.\n", - }, - { - name: 'request.dest_class', - type: 'long', - description: "The destination class id in 'slab reassign' command.\n", - }, - { - name: 'request.automove', - type: 'keyword', - description: - 'The automove mode in the \'slab automove\' command expressed as a string. This value can be "standby"(=0), "slow"(=1), "aggressive"(=2), or the raw value if the value is unknown.\n', - }, - { - name: 'request.flags', - type: 'long', - description: 'The memcache command flags sent in the request (if present).\n', - }, - { - name: 'response.flags', - type: 'long', - description: 'The memcache message flags sent in the response (if present).\n', - }, - { - name: 'request.exptime', - type: 'long', - description: - 'The data expiry time in seconds sent with the memcache command (if present). If the value is <30 days, the expiry time is relative to "now", or else it is an absolute Unix time in seconds (32-bit).\n', - }, - { - name: 'request.sleep_us', - type: 'long', - description: "The sleep setting in microseconds for the 'lru_crawler sleep' command.\n", - }, - { - name: 'response.value', - type: 'long', - description: 'The counter value returned by a counter operation.\n', - }, - { - name: 'request.noreply', - type: 'boolean', - description: - 'Set to true if noreply was set in the request. The `memcache.response` field will be missing.\n', - }, - { - name: 'request.quiet', - type: 'boolean', - description: - 'Set to true if the binary protocol message is to be treated as a quiet message.\n', - }, - { - name: 'request.cas_unique', - type: 'long', - description: 'The CAS (compare-and-swap) identifier if present.\n', - }, - { - name: 'response.cas_unique', - type: 'long', - description: - 'The CAS (compare-and-swap) identifier to be used with CAS-based updates (if present).\n', - }, - { - name: 'response.stats', - type: 'array', - description: - 'The list of statistic values returned. Each entry is a dictionary with the fields "name" and "value".\n', - }, - { - name: 'response.version', - type: 'keyword', - description: 'The returned memcache version string.\n', - }, - ], - }, - ], - }, - { - key: 'mongodb', - title: 'MongoDb', - description: - 'MongoDB-specific event fields. These fields mirror closely the fields for the MongoDB wire protocol. The higher level fields (for example, `query` and `resource`) apply to MongoDB events as well.\n', - fields: [ - { - name: 'mongodb', - type: 'group', - fields: [ - { - name: 'error', - description: - 'If the MongoDB request has resulted in an error, this field contains the error message returned by the server.\n', - }, - { - name: 'fullCollectionName', - description: - 'The full collection name. The full collection name is the concatenation of the database name with the collection name, using a dot (.) for the concatenation. For example, for the database foo and the collection bar, the full collection name is foo.bar.\n', - }, - { - name: 'numberToSkip', - type: 'long', - description: - 'Sets the number of documents to omit - starting from the first document in the resulting dataset - when returning the result of the query.\n', - }, - { - name: 'numberToReturn', - type: 'long', - description: 'The requested maximum number of documents to be returned.\n', - }, - { - name: 'numberReturned', - type: 'long', - description: 'The number of documents in the reply.\n', - }, - { - name: 'startingFrom', - description: 'Where in the cursor this reply is starting.\n', - }, - { - name: 'query', - description: - 'A JSON document that represents the query. The query will contain one or more elements, all of which must match for a document to be included in the result set. Possible elements include $query, $orderby, $hint, $explain, and $snapshot.\n', - }, - { - name: 'returnFieldsSelector', - description: - 'A JSON document that limits the fields in the returned documents. The returnFieldsSelector contains one or more elements, each of which is the name of a field that should be returned, and the integer value 1.\n', - }, - { - name: 'selector', - description: - 'A BSON document that specifies the query for selecting the document to update or delete.\n', - }, - { - name: 'update', - description: - 'A BSON document that specifies the update to be performed. For information on specifying updates, see the Update Operations documentation from the MongoDB Manual.\n', - }, - { - name: 'cursorId', - description: - 'The cursor identifier returned in the OP_REPLY. This must be the value that was returned from the database.\n', - }, - ], - }, - ], - }, - { - key: 'mysql', - title: 'MySQL', - description: 'MySQL-specific event fields.\n', - fields: [ - { - name: 'mysql', - type: 'group', - fields: [ - { - name: 'affected_rows', - type: 'long', - description: - 'If the MySQL command is successful, this field contains the affected number of rows of the last statement.\n', - }, - { - name: 'insert_id', - description: - 'If the INSERT query is successful, this field contains the id of the newly inserted row.\n', - }, - { - name: 'num_fields', - description: - 'If the SELECT query is successful, this field is set to the number of fields returned.\n', - }, - { - name: 'num_rows', - description: - 'If the SELECT query is successful, this field is set to the number of rows returned.\n', - }, - { - name: 'query', - description: "The row mysql query as read from the transaction's request.\n", - }, - { - name: 'error_code', - type: 'long', - description: 'The error code returned by MySQL.\n', - }, - { - name: 'error_message', - description: 'The error info message returned by MySQL.\n', - }, - ], - }, - ], - }, - { - key: 'nfs', - title: 'NFS', - description: 'NFS v4/3 specific event fields.', - fields: [ - { - name: 'nfs', - type: 'group', - fields: [ - { - name: 'version', - type: 'long', - description: 'NFS protocol version number.', - }, - { - name: 'minor_version', - type: 'long', - description: 'NFS protocol minor version number.', - }, - { - name: 'tag', - description: 'NFS v4 COMPOUND operation tag.', - }, - { - name: 'opcode', - description: 'NFS operation name, or main operation name, in case of COMPOUND calls.\n', - }, - { - name: 'status', - description: 'NFS operation reply status.', - }, - ], - }, - { - name: 'rpc', - type: 'group', - description: 'ONC RPC specific event fields.', - fields: [ - { - name: 'xid', - description: 'RPC message transaction identifier.', - }, - { - name: 'status', - description: 'RPC message reply status.', - }, - { - name: 'auth_flavor', - description: 'RPC authentication flavor.', - }, - { - name: 'cred.uid', - type: 'long', - description: "RPC caller's user id, in case of auth-unix.", - }, - { - name: 'cred.gid', - type: 'long', - description: "RPC caller's group id, in case of auth-unix.", - }, - { - name: 'cred.gids', - description: "RPC caller's secondary group ids, in case of auth-unix.", - }, - { - name: 'cred.stamp', - type: 'long', - description: 'Arbitrary ID which the caller machine may generate.', - }, - { - name: 'cred.machinename', - description: "The name of the caller's machine.", - }, - { - name: 'call_size', - type: 'alias', - path: 'source.bytes', - migration: true, - description: 'RPC call size with argument.', - }, - { - name: 'reply_size', - type: 'alias', - path: 'destination.bytes', - migration: true, - description: 'RPC reply size with argument.', - }, - ], - }, - ], - }, - { - key: 'pgsql', - title: 'PostgreSQL', - description: 'PostgreSQL-specific event fields.\n', - fields: [ - { - name: 'pgsql', - type: 'group', - fields: [ - { - name: 'error_code', - description: 'The PostgreSQL error code.', - type: 'long', - }, - { - name: 'error_message', - description: 'The PostgreSQL error message.', - }, - { - name: 'error_severity', - description: 'The PostgreSQL error severity.', - possible_values: ['ERROR', 'FATAL', 'PANIC'], - }, - { - name: 'num_fields', - description: - 'If the SELECT query if successful, this field is set to the number of fields returned.\n', - }, - { - name: 'num_rows', - description: - 'If the SELECT query if successful, this field is set to the number of rows returned.\n', - }, - ], - }, - ], - }, - { - key: 'redis', - title: 'Redis', - description: 'Redis-specific event fields.\n', - fields: [ - { - name: 'redis', - type: 'group', - fields: [ - { - name: 'return_value', - description: 'The return value of the Redis command in a human readable format.\n', - }, - { - name: 'error', - description: - 'If the Redis command has resulted in an error, this field contains the error message returned by the Redis server.\n', - }, - ], - }, - ], - }, - { - key: 'thrift', - title: 'Thrift-RPC', - description: 'Thrift-RPC specific event fields.\n', - fields: [ - { - name: 'thrift', - type: 'group', - fields: [ - { - name: 'params', - description: - 'The RPC method call parameters in a human readable format. If the IDL files are available, the parameters use names whenever possible. Otherwise, the IDs from the message are used.\n', - }, - { - name: 'service', - description: 'The name of the Thrift-RPC service as defined in the IDL files.\n', - }, - { - name: 'return_value', - description: - 'The value returned by the Thrift-RPC call. This is encoded in a human readable format.\n', - }, - { - name: 'exceptions', - description: - 'If the call resulted in exceptions, this field contains the exceptions in a human readable format.\n', - }, - ], - }, - ], - }, - { - key: 'tls_detailed', - title: 'Detailed TLS', - description: 'Detailed TLS-specific event fields.\n', - fields: [ - { - name: 'tls', - type: 'group', - fields: [ - { - name: 'detailed', - type: 'group', - default_fields: false, - fields: [ - { - name: 'version', - type: 'keyword', - description: 'The version of the TLS protocol used.\n', - example: 'TLS 1.3', - }, - { - name: 'resumption_method', - type: 'keyword', - description: - 'If the session has been resumed, the underlying method used. One of "id" for TLS session ID or "ticket" for TLS ticket extension.\n', - }, - { - name: 'client_certificate_requested', - type: 'boolean', - description: - 'Whether the server has requested the client to authenticate itself using a client certificate.\n', - }, - { - name: 'client_hello', - type: 'group', - fields: [ - { - name: 'version', - type: 'keyword', - description: - 'The version of the TLS protocol by which the client wishes to communicate during this session.\n', - }, - { - name: 'session_id', - type: 'keyword', - description: - 'Unique number to identify the session for the corresponding connection with the client.\n', - }, - { - name: 'supported_compression_methods', - type: 'keyword', - description: - 'The list of compression methods the client supports. See https://www.iana.org/assignments/comp-meth-ids/comp-meth-ids.xhtml\n', - }, - { - name: 'extensions', - type: 'group', - description: 'The hello extensions provided by the client.', - fields: [ - { - name: 'server_name_indication', - type: 'keyword', - description: 'List of hostnames', - }, - { - name: 'application_layer_protocol_negotiation', - type: 'keyword', - description: - 'List of application-layer protocols the client is willing to use.\n', - }, - { - name: 'session_ticket', - type: 'keyword', - description: - 'Length of the session ticket, if provided, or an empty string to advertise support for tickets.\n', - }, - { - name: 'supported_versions', - type: 'keyword', - description: 'List of TLS versions that the client is willing to use.\n', - }, - { - name: 'supported_groups', - type: 'keyword', - description: - 'List of Elliptic Curve Cryptography (ECC) curve groups supported by the client.\n', - }, - { - name: 'signature_algorithms', - type: 'keyword', - description: - 'List of signature algorithms that may be use in digital signatures.\n', - }, - { - name: 'ec_points_formats', - type: 'keyword', - description: - 'List of Elliptic Curve (EC) point formats. Indicates the set of point formats that the client can parse.\n', - }, - { - name: '_unparsed_', - type: 'keyword', - description: 'List of extensions that were left unparsed by Packetbeat.\n', - }, - ], - }, - ], - }, - { - name: 'server_hello', - type: 'group', - fields: [ - { - name: 'version', - type: 'keyword', - description: - 'The version of the TLS protocol that is used for this session. It is the highest version supported by the server not exceeding the version requested in the client hello.\n', - }, - { - name: 'selected_compression_method', - type: 'keyword', - description: - 'The compression method selected by the server from the list provided in the client hello.\n', - }, - { - name: 'session_id', - type: 'keyword', - description: - 'Unique number to identify the session for the corresponding connection with the client.\n', - }, - { - name: 'extensions', - type: 'group', - description: 'The hello extensions provided by the server.', - fields: [ - { - name: 'application_layer_protocol_negotiation', - type: 'keyword', - description: 'Negotiated application layer protocol', - }, - { - name: 'session_ticket', - type: 'keyword', - description: - 'Used to announce that a session ticket will be provided by the server. Always an empty string.\n', - }, - { - name: 'supported_versions', - type: 'keyword', - description: 'Negotiated TLS version to be used.\n', - }, - { - name: 'ec_points_formats', - type: 'keyword', - description: - 'List of Elliptic Curve (EC) point formats. Indicates the set of point formats that the server can parse.\n', - }, - { - name: '_unparsed_', - type: 'keyword', - description: 'List of extensions that were left unparsed by Packetbeat.\n', - }, - ], - }, - ], - }, - { - name: 'client_certificate', - type: 'group', - description: 'Certificate provided by the client for authentication.', - fields: [ - { - name: 'version', - type: 'long', - description: 'X509 format version.', - }, - { - name: 'serial_number', - type: 'keyword', - description: "The certificate's serial number.", - }, - { - name: 'not_before', - type: 'date', - description: 'Date before which the certificate is not valid.', - }, - { - name: 'not_after', - type: 'date', - description: 'Date after which the certificate expires.', - }, - { - name: 'public_key_algorithm', - type: 'keyword', - description: - "The algorithm used for this certificate's public key. One of RSA, DSA or ECDSA.\n", - }, - { - name: 'public_key_size', - type: 'long', - description: 'Size of the public key.', - }, - { - name: 'signature_algorithm', - type: 'keyword', - description: "The algorithm used for the certificate's signature.\n", - }, - { - name: 'alternative_names', - type: 'keyword', - description: 'Subject Alternative Names for this certificate.', - }, - { - name: 'subject', - type: 'group', - description: 'Subject represented by this certificate.', - fields: [ - { - name: 'country', - type: 'keyword', - description: 'Country code.', - }, - { - name: 'organization', - type: 'keyword', - description: 'Organization name.', - }, - { - name: 'organizational_unit', - type: 'keyword', - description: 'Unit within organization.', - }, - { - name: 'province', - type: 'keyword', - description: 'Province or region within country.', - }, - { - name: 'common_name', - type: 'keyword', - description: 'Name or host name identified by the certificate.', - }, - { - name: 'locality', - type: 'keyword', - description: 'Locality.', - }, - ], - }, - { - name: 'issuer', - type: 'group', - description: 'Entity that issued and signed this certificate.', - fields: [ - { - name: 'country', - type: 'keyword', - description: 'Country code.', - }, - { - name: 'organization', - type: 'keyword', - description: 'Organization name.', - }, - { - name: 'organizational_unit', - type: 'keyword', - description: 'Unit within organization.', - }, - { - name: 'province', - type: 'keyword', - description: 'Province or region within country.', - }, - { - name: 'common_name', - type: 'keyword', - description: 'Name or host name identified by the certificate.', - }, - { - name: 'locality', - type: 'keyword', - description: 'Locality.', - }, - ], - }, - ], - }, - { - name: 'server_certificate', - type: 'group', - description: 'Certificate provided by the server for authentication.', - fields: [ - { - name: 'version', - type: 'long', - description: 'X509 format version.', - }, - { - name: 'serial_number', - type: 'keyword', - description: "The certificate's serial number.", - }, - { - name: 'not_before', - type: 'date', - description: 'Date before which the certificate is not valid.', - }, - { - name: 'not_after', - type: 'date', - description: 'Date after which the certificate expires.', - }, - { - name: 'public_key_algorithm', - type: 'keyword', - description: - "The algorithm used for this certificate's public key. One of RSA, DSA or ECDSA.\n", - }, - { - name: 'public_key_size', - type: 'long', - description: 'Size of the public key.', - }, - { - name: 'signature_algorithm', - type: 'keyword', - description: "The algorithm used for the certificate's signature.\n", - }, - { - name: 'alternative_names', - type: 'keyword', - description: 'Subject Alternative Names for this certificate.', - }, - { - name: 'subject', - type: 'group', - description: 'Subject represented by this certificate.', - fields: [ - { - name: 'country', - type: 'keyword', - description: 'Country code.', - }, - { - name: 'organization', - type: 'keyword', - description: 'Organization name.', - }, - { - name: 'organizational_unit', - type: 'keyword', - description: 'Unit within organization.', - }, - { - name: 'province', - type: 'keyword', - description: 'Province or region within country.', - }, - { - name: 'common_name', - type: 'keyword', - description: 'Name or host name identified by the certificate.', - }, - { - name: 'locality', - type: 'keyword', - description: 'Locality.', - }, - ], - }, - { - name: 'issuer', - type: 'group', - description: 'Entity that issued and signed this certificate.', - fields: [ - { - name: 'country', - type: 'keyword', - description: 'Country code.', - }, - { - name: 'organization', - type: 'keyword', - description: 'Organization name.', - }, - { - name: 'organizational_unit', - type: 'keyword', - description: 'Unit within organization.', - }, - { - name: 'province', - type: 'keyword', - description: 'Province or region within country.', - }, - { - name: 'common_name', - type: 'keyword', - description: 'Name or host name identified by the certificate.', - }, - { - name: 'locality', - type: 'keyword', - description: 'Locality.', - }, - ], - }, - ], - }, - { - name: 'server_certificate_chain', - type: 'array', - description: 'Chain of trust for the server certificate.', - }, - { - name: 'client_certificate_chain', - type: 'array', - description: 'Chain of trust for the client certificate.', - }, - { - name: 'alert_types', - type: 'keyword', - description: 'An array containing the TLS alert type for every alert received.\n', - }, - ], - }, - ], - }, - { - name: 'tls.handshake_completed', - type: 'alias', - path: 'tls.established', - }, - { - name: 'tls.client_hello.supported_ciphers', - type: 'alias', - path: 'tls.client.supported_ciphers', - }, - { - name: 'tls.server_hello.selected_cipher', - type: 'alias', - path: 'tls.cipher', - }, - { - name: 'tls.fingerprints.ja3', - type: 'alias', - path: 'tls.client.ja3', - }, - { - name: 'tls.resumption_method', - type: 'alias', - path: 'tls.detailed.resumption_method', - }, - { - name: 'tls.client_certificate_requested', - type: 'alias', - path: 'tls.detailed.client_certificate_requested', - }, - { - name: 'tls.client_hello.version', - type: 'alias', - path: 'tls.detailed.client_hello.version', - }, - { - name: 'tls.client_hello.session_id', - type: 'alias', - path: 'tls.detailed.client_hello.session_id', - }, - { - name: 'tls.client_hello.supported_compression_methods', - type: 'alias', - path: 'tls.detailed.client_hello.supported_compression_methods', - }, - { - name: 'tls.client_hello.extensions.server_name_indication', - type: 'alias', - path: 'tls.detailed.client_hello.extensions.server_name_indication', - }, - { - name: 'tls.client_hello.extensions.application_layer_protocol_negotiation', - type: 'alias', - path: 'tls.detailed.client_hello.extensions.application_layer_protocol_negotiation', - }, - { - name: 'tls.client_hello.extensions.session_ticket', - type: 'alias', - path: 'tls.detailed.client_hello.extensions.session_ticket', - }, - { - name: 'tls.client_hello.extensions.supported_versions', - type: 'alias', - path: 'tls.detailed.client_hello.extensions.supported_versions', - }, - { - name: 'tls.client_hello.extensions.supported_groups', - type: 'alias', - path: 'tls.detailed.client_hello.extensions.supported_groups', - }, - { - name: 'tls.client_hello.extensions.signature_algorithms', - type: 'alias', - path: 'tls.detailed.client_hello.extensions.signature_algorithms', - }, - { - name: 'tls.client_hello.extensions.ec_points_formats', - type: 'alias', - path: 'tls.detailed.client_hello.extensions.ec_points_formats', - }, - { - name: 'tls.client_hello.extensions._unparsed_', - type: 'alias', - path: 'tls.detailed.client_hello.extensions._unparsed_', - }, - { - name: 'tls.server_hello.version', - type: 'alias', - path: 'tls.detailed.server_hello.version', - }, - { - name: 'tls.server_hello.selected_compression_method', - type: 'alias', - path: 'tls.detailed.server_hello.selected_compression_method', - }, - { - name: 'tls.server_hello.session_id', - type: 'alias', - path: 'tls.detailed.server_hello.session_id', - }, - { - name: 'tls.server_hello.extensions.application_layer_protocol_negotiation', - type: 'alias', - path: 'tls.detailed.server_hello.extensions.application_layer_protocol_negotiation', - }, - { - name: 'tls.server_hello.extensions.session_ticket', - type: 'alias', - path: 'tls.detailed.server_hello.extensions.session_ticket', - }, - { - name: 'tls.server_hello.extensions.supported_versions', - type: 'alias', - path: 'tls.detailed.server_hello.extensions.supported_versions', - }, - { - name: 'tls.server_hello.extensions.ec_points_formats', - type: 'alias', - path: 'tls.detailed.server_hello.extensions.ec_points_formats', - }, - { - name: 'tls.server_hello.extensions._unparsed_', - type: 'alias', - path: 'tls.detailed.server_hello.extensions._unparsed_', - }, - { - name: 'tls.client_certificate.version', - type: 'alias', - path: 'tls.detailed.client_certificate.version', - }, - { - name: 'tls.client_certificate.serial_number', - type: 'alias', - path: 'tls.detailed.client_certificate.serial_number', - }, - { - name: 'tls.client_certificate.not_before', - type: 'alias', - path: 'tls.detailed.client_certificate.not_before', - }, - { - name: 'tls.client_certificate.not_after', - type: 'alias', - path: 'tls.detailed.client_certificate.not_after', - }, - { - name: 'tls.client_certificate.public_key_algorithm', - type: 'alias', - path: 'tls.detailed.client_certificate.public_key_algorithm', - }, - { - name: 'tls.client_certificate.public_key_size', - type: 'alias', - path: 'tls.detailed.client_certificate.public_key_size', - }, - { - name: 'tls.client_certificate.signature_algorithm', - type: 'alias', - path: 'tls.detailed.client_certificate.signature_algorithm', - }, - { - name: 'tls.client_certificate.alternative_names', - type: 'alias', - path: 'tls.detailed.client_certificate.alternative_names', - }, - { - name: 'tls.client_certificate.subject.country', - type: 'alias', - path: 'tls.detailed.client_certificate.subject.country', - }, - { - name: 'tls.client_certificate.subject.organization', - type: 'alias', - path: 'tls.detailed.client_certificate.subject.organization', - }, - { - name: 'tls.client_certificate.subject.organizational_unit', - type: 'alias', - path: 'tls.detailed.client_certificate.subject.organizational_unit', - }, - { - name: 'tls.client_certificate.subject.province', - type: 'alias', - path: 'tls.detailed.client_certificate.subject.province', - }, - { - name: 'tls.client_certificate.subject.common_name', - type: 'alias', - path: 'tls.detailed.client_certificate.subject.common_name', - }, - { - name: 'tls.client_certificate.subject.locality', - type: 'alias', - path: 'tls.detailed.client_certificate.subject.locality', - }, - { - name: 'tls.client_certificate.issuer.country', - type: 'alias', - path: 'tls.detailed.client_certificate.issuer.country', - }, - { - name: 'tls.client_certificate.issuer.organization', - type: 'alias', - path: 'tls.detailed.client_certificate.issuer.organization', - }, - { - name: 'tls.client_certificate.issuer.organizational_unit', - type: 'alias', - path: 'tls.detailed.client_certificate.issuer.organizational_unit', - }, - { - name: 'tls.client_certificate.issuer.province', - type: 'alias', - path: 'tls.detailed.client_certificate.issuer.province', - }, - { - name: 'tls.client_certificate.issuer.common_name', - type: 'alias', - path: 'tls.detailed.client_certificate.issuer.common_name', - }, - { - name: 'tls.client_certificate.issuer.locality', - type: 'alias', - path: 'tls.detailed.client_certificate.issuer.locality', - }, - { - name: 'tls.server_certificate.version', - type: 'alias', - path: 'tls.detailed.server_certificate.version', - }, - { - name: 'tls.server_certificate.serial_number', - type: 'alias', - path: 'tls.detailed.server_certificate.serial_number', - }, - { - name: 'tls.server_certificate.not_before', - type: 'alias', - path: 'tls.detailed.server_certificate.not_before', - }, - { - name: 'tls.server_certificate.not_after', - type: 'alias', - path: 'tls.detailed.server_certificate.not_after', - }, - { - name: 'tls.server_certificate.public_key_algorithm', - type: 'alias', - path: 'tls.detailed.server_certificate.public_key_algorithm', - }, - { - name: 'tls.server_certificate.public_key_size', - type: 'alias', - path: 'tls.detailed.server_certificate.public_key_size', - }, - { - name: 'tls.server_certificate.signature_algorithm', - type: 'alias', - path: 'tls.detailed.server_certificate.signature_algorithm', - }, - { - name: 'tls.server_certificate.alternative_names', - type: 'alias', - path: 'tls.detailed.server_certificate.alternative_names', - }, - { - name: 'tls.server_certificate.subject.country', - type: 'alias', - path: 'tls.detailed.server_certificate.subject.country', - }, - { - name: 'tls.server_certificate.subject.organization', - type: 'alias', - path: 'tls.detailed.server_certificate.subject.organization', - }, - { - name: 'tls.server_certificate.subject.organizational_unit', - type: 'alias', - path: 'tls.detailed.server_certificate.subject.organizational_unit', - }, - { - name: 'tls.server_certificate.subject.province', - type: 'alias', - path: 'tls.detailed.server_certificate.subject.province', - }, - { - name: 'tls.server_certificate.subject.common_name', - type: 'alias', - path: 'tls.detailed.server_certificate.subject.common_name', - }, - { - name: 'tls.server_certificate.subject.locality', - type: 'alias', - path: 'tls.detailed.server_certificate.subject.locality', - }, - { - name: 'tls.server_certificate.issuer.country', - type: 'alias', - path: 'tls.detailed.server_certificate.issuer.country', - }, - { - name: 'tls.server_certificate.issuer.organization', - type: 'alias', - path: 'tls.detailed.server_certificate.issuer.organization', - }, - { - name: 'tls.server_certificate.issuer.organizational_unit', - type: 'alias', - path: 'tls.detailed.server_certificate.issuer.organizational_unit', - }, - { - name: 'tls.server_certificate.issuer.province', - type: 'alias', - path: 'tls.detailed.server_certificate.issuer.province', - }, - { - name: 'tls.server_certificate.issuer.common_name', - type: 'alias', - path: 'tls.detailed.server_certificate.issuer.common_name', - }, - { - name: 'tls.server_certificate.issuer.locality', - type: 'alias', - path: 'tls.detailed.server_certificate.issuer.locality', - }, - { - name: 'tls.alert_types', - type: 'alias', - path: 'tls.detailed.alert_types', - }, - ], - }, -]; diff --git a/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/winlogbeat.ts b/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/winlogbeat.ts deleted file mode 100644 index 7457cb3f4428fd..00000000000000 --- a/x-pack/plugins/security_solution/server/utils/beat_schema/8.0.0/winlogbeat.ts +++ /dev/null @@ -1,2844 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/** - * An instance of the unmodified schema exported from winlogbeat-8.0.0-SNAPSHOT-windows-x86_64.zip - * - */ - -import { Schema } from '../type'; - -export const winlogbeatSchema: Schema = [ - { - key: 'ecs', - title: 'ECS', - description: 'ECS Fields.', - fields: [ - { - name: '@timestamp', - level: 'core', - required: true, - type: 'date', - description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', - example: '2016-05-23T08:05:34.853Z', - }, - { - name: 'labels', - level: 'core', - type: 'object', - object_type: 'keyword', - description: - 'Custom key/value pairs.\n\nCan be used to add meta information to events. Should not contain nested objects.\nAll values are stored as keyword.\n\nExample: `docker` and `k8s` labels.', - example: { - application: 'foo-bar', - env: 'production', - }, - }, - { - name: 'message', - level: 'core', - type: 'text', - description: - 'For log events the message field contains the log message, optimized\nfor viewing in a log viewer.\n\nFor structured logs without an original message field, other fields can be concatenated\nto form a human-readable summary of the event.\n\nIf multiple messages exist, they can be combined into one message.', - example: 'Hello World', - }, - { - name: 'tags', - level: 'core', - type: 'keyword', - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - }, - { - name: 'agent', - title: 'Agent', - group: 2, - description: - 'The agent fields contain the data about the software entity, if\nany, that collects, detects, or observes events on a host, or takes measurements\non a host.\n\nExamples include Beats. Agents may also run on observers. ECS agent.* fields\nshall be populated with details of the agent running on the host or observer\nwhere the event happened or the measurement was taken.', - footnote: - 'Examples: In the case of Beats for logs, the agent.name is filebeat.\nFor APM, it is the agent running in the app/service. The agent information does\nnot change if data is sent through queuing systems like Kafka, Redis, or processing\nsystems such as Logstash or APM Server.', - type: 'group', - fields: [ - { - name: 'ephemeral_id', - level: 'extended', - type: 'keyword', - description: - 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - description: - 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', - example: '8a4f500d', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', - example: 'foo', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', - example: 'filebeat', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - description: 'Version of the agent.', - example: '6.0.0-rc2', - }, - ], - }, - { - name: 'client', - title: 'Client', - group: 2, - description: - 'A client is defined as the initiator of a network connection for\nevents regarding sessions, connections, or bidirectional flow records.\n\nFor TCP events, the client is the initiator of the TCP connection that sends\nthe SYN packet(s). For other protocols, the client is generally the initiator\nor requestor in the network transaction. Some systems use the term "originator"\nto refer the client in TCP connections. The client fields describe details about\nthe system acting as the client in the network event. Client fields are usually\npopulated in conjunction with server fields. Client fields are generally not\npopulated for packet-level events.\n\nClient / server representations can add semantic context to an exchange, which\nis helpful to visualize the data in certain situations. If your context falls\nin that category, you should still ensure that source and destination are filled\nappropriately.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - description: - 'Some event client addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the client to the server.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - description: 'Client domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the client.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - description: 'MAC address of the client.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the client to the server.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the client.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'cloud', - title: 'Cloud', - group: 2, - description: 'Fields related to the cloud or infrastructure the events are coming\nfrom.', - footnote: - 'Examples: If Metricbeat is running on an EC2 host and fetches data\nfrom its host, the cloud info contains the data about this machine. If Metricbeat\nruns on a remote machine outside the cloud and fetches data from a service running\nin the cloud, the field contains cloud data from the machine the service is\nrunning on.', - type: 'group', - fields: [ - { - name: 'account.id', - level: 'extended', - type: 'keyword', - description: - 'The cloud account or organization id used to identify different\nentities in a multi-tenant environment.\n\nExamples: AWS account id, Google Cloud ORG Id, or other unique identifier.', - example: 666777888999, - }, - { - name: 'availability_zone', - level: 'extended', - type: 'keyword', - description: 'Availability zone in which this host is running.', - example: 'us-east-1c', - }, - { - name: 'instance.id', - level: 'extended', - type: 'keyword', - description: 'Instance ID of the host machine.', - example: 'i-1234567890abcdef0', - }, - { - name: 'instance.name', - level: 'extended', - type: 'keyword', - description: 'Instance name of the host machine.', - }, - { - name: 'machine.type', - level: 'extended', - type: 'keyword', - description: 'Machine type of the host machine.', - example: 't2.medium', - }, - { - name: 'provider', - level: 'extended', - type: 'keyword', - description: - 'Name of the cloud provider. Example values are aws, azure, gcp,\nor digitalocean.', - example: 'aws', - }, - { - name: 'region', - level: 'extended', - type: 'keyword', - description: 'Region in which this host is running.', - example: 'us-east-1', - }, - ], - }, - { - name: 'container', - title: 'Container', - group: 2, - description: - 'Container fields are used for meta information about the specific\ncontainer that is the source of information.\n\nThese fields help correlate data based containers from any runtime.', - type: 'group', - fields: [ - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Unique container id.', - }, - { - name: 'image.name', - level: 'extended', - type: 'keyword', - description: 'Name of the image the container was built on.', - }, - { - name: 'image.tag', - level: 'extended', - type: 'keyword', - description: 'Container image tag.', - }, - { - name: 'labels', - level: 'extended', - type: 'object', - object_type: 'keyword', - description: 'Image labels.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - description: 'Container name.', - }, - { - name: 'runtime', - level: 'extended', - type: 'keyword', - description: 'Runtime managing this container.', - example: 'docker', - }, - ], - }, - { - name: 'destination', - title: 'Destination', - group: 2, - description: - 'Destination fields describe details about the destination of a packet/event.\n\nDestination fields are usually populated in conjunction with source fields.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - description: - 'Some event destination addresses are defined ambiguously. The\nevent will sometimes list an IP, a domain or a unix socket. You should always\nstore the raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the destination to the source.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - description: 'Destination domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the destination.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - description: 'MAC address of the destination.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the destination to the source.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the destination.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'ecs', - title: 'ECS', - group: 2, - description: 'Meta-information specific to ECS.', - type: 'group', - fields: [ - { - name: 'version', - level: 'core', - required: true, - type: 'keyword', - description: - 'ECS version this event conforms to. `ecs.version` is a required\nfield and must exist in all events.\n\nWhen querying across multiple indices -- which may conform to slightly different\nECS versions -- this field lets integrations adjust to the schema version\nof the events.', - example: '1.0.0', - }, - ], - }, - { - name: 'error', - title: 'Error', - group: 2, - description: - 'These fields can represent errors of any kind.\n\nUse them for errors that happen while fetching events or in cases where the\nevent itself contains an error.', - type: 'group', - fields: [ - { - name: 'code', - level: 'core', - type: 'keyword', - description: 'Error code describing the error.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Unique identifier for the error.', - }, - { - name: 'message', - level: 'core', - type: 'text', - description: 'Error message.', - }, - ], - }, - { - name: 'event', - title: 'Event', - group: 2, - description: - 'The event fields are used for context information about the log\nor metric event itself.\n\nA log is defined as an event containing details of something that happened.\nLog events must include the time at which the thing happened. Examples of log\nevents include a process starting on a host, a network packet being sent from\na source to a destination, or a network connection between a client and a server\nbeing initiated or closed. A metric is defined as an event containing one or\nmore numerical or categorical measurements and the time at which the measurement\nwas taken. Examples of metric events include memory pressure measured on a host,\nor vulnerabilities measured on a scanned host.', - type: 'group', - fields: [ - { - name: 'action', - level: 'core', - type: 'keyword', - description: - 'The action captured by the event.\n\nThis describes the information in the event. It is more specific than `event.category`.\nExamples are `group-add`, `process-started`, `file-created`. The value is\nnormally defined by the implementer.', - example: 'user-password-change', - }, - { - name: 'category', - level: 'core', - type: 'keyword', - description: - 'Event category.\n\nThis contains high-level information about the contents of the event. It is\nmore generic than `event.action`, in the sense that typically a category contains\nmultiple actions. Warning: In future versions of ECS, we plan to provide a\nlist of acceptable values for this field, please use with caution.', - example: 'user-management', - }, - { - name: 'created', - level: 'core', - type: 'date', - description: - 'event.created contains the date/time when the event was first\nread by an agent, or by your pipeline.\n\nThis field is distinct from @timestamp in that @timestamp typically contain\nthe time extracted from the original event.\n\nIn most situations, these two timestamps will be slightly different. The difference\ncan be used to calculate the delay between your source generating an event,\nand the time when your agent first processed it. This can be used to monitor\nyour agents or pipelines ability to keep up with your event source.\n\nIn case the two timestamps are identical, @timestamp should be used.', - }, - { - name: 'dataset', - level: 'core', - type: 'keyword', - description: - 'Name of the dataset.\n\nThe concept of a `dataset` (fileset / metricset) is used in Beats as a subset\nof modules. It contains the information which is currently stored in metricset.name\nand metricset.module or fileset.name.', - example: 'stats', - }, - { - name: 'duration', - level: 'core', - type: 'long', - format: 'duration', - input_format: 'nanoseconds', - description: - 'Duration of the event in nanoseconds.\n\nIf event.start and event.end are known this value should be the difference\nbetween the end and start time.', - }, - { - name: 'end', - level: 'extended', - type: 'date', - description: - 'event.end contains the date when the event ended or when the activity\nwas last observed.', - }, - { - name: 'hash', - level: 'extended', - type: 'keyword', - description: - 'Hash (perhaps logstash fingerprint) of raw field to be able to\ndemonstrate log integrity.', - example: '123456789012345678901234567890ABCD', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'Unique ID to describe the event.', - example: '8a4f500d', - }, - { - name: 'kind', - level: 'extended', - type: 'keyword', - description: - 'The kind of the event.\n\nThis gives information about what type of information the event contains,\nwithout being specific to the contents of the event. Examples are `event`,\n`state`, `alarm`. Warning: In future versions of ECS, we plan to provide a\nlist of acceptable values for this field, please use with caution.', - example: 'state', - }, - { - name: 'module', - level: 'core', - type: 'keyword', - description: - 'Name of the module this data is coming from.\n\nThis information is coming from the modules used in Beats or Logstash.', - example: 'mysql', - }, - { - name: 'original', - level: 'core', - type: 'keyword', - description: - 'Raw text message of entire event. Used to demonstrate log integrity.\n\nThis field is not indexed and doc_values are disabled. It cannot be searched,\nbut it can be retrieved from `_source`.', - example: - 'Sep 19 08:26:10 host CEF:0|Security| threatmanager|1.0|100|\nworm successfully stopped|10|src=10.0.0.1 dst=2.1.2.2spt=1232', - }, - { - name: 'outcome', - level: 'extended', - type: 'keyword', - description: - 'The outcome of the event.\n\nIf the event describes an action, this fields contains the outcome of that\naction. Examples outcomes are `success` and `failure`. Warning: In future\nversions of ECS, we plan to provide a list of acceptable values for this field,\nplease use with caution.', - example: 'success', - }, - { - name: 'risk_score', - level: 'core', - type: 'float', - description: - "Risk score or priority of the event (e.g. security solutions).\nUse your system's original value here.", - }, - { - name: 'risk_score_norm', - level: 'extended', - type: 'float', - description: - 'Normalized risk score or priority of the event, on a scale of\n0 to 100.\n\nThis is mainly useful if you use more than one system that assigns risk scores,\nand you want to see a normalized value across all systems.', - }, - { - name: 'severity', - level: 'core', - type: 'long', - format: 'string', - description: - "Severity describes the original severity of the event. What the\ndifferent severity values mean can very different between use cases. It's\nup to the implementer to make sure severities are consistent across events.", - example: '7', - }, - { - name: 'start', - level: 'extended', - type: 'date', - description: - 'event.start contains the date when the event started or when the\nactivity was first observed.', - }, - { - name: 'timezone', - level: 'extended', - type: 'keyword', - description: - 'This field should be populated when the events timestamp does\nnot include timezone information already (e.g. default Syslog timestamps).\nIts optional otherwise.\n\nAcceptable timezone formats are: a canonical ID (e.g. "Europe/Amsterdam"),\nabbreviated (e.g. "EST") or an HH:mm differential (e.g. "-05:00").', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - description: - 'Reserved for future usage.\n\nPlease avoid using this field for user data.', - }, - ], - }, - { - name: 'file', - title: 'File', - group: 2, - description: - 'A file is defined as a set of information that has been created\non, or has existed on a filesystem.\n\nFile objects can be associated with host events, network events, and/or file\nevents (e.g., those produced by File Integrity Monitoring [FIM] products or\nservices). File fields provide details about the affected file associated with\nthe event or metric.', - type: 'group', - fields: [ - { - name: 'ctime', - level: 'extended', - type: 'date', - description: 'Last time file metadata changed.', - }, - { - name: 'device', - level: 'extended', - type: 'keyword', - description: 'Device that is the source of the file.', - }, - { - name: 'extension', - level: 'extended', - type: 'keyword', - description: 'File extension.\n\nThis should allow easy filtering by file extensions.', - example: 'png', - }, - { - name: 'gid', - level: 'extended', - type: 'keyword', - description: 'Primary group ID (GID) of the file.', - }, - { - name: 'group', - level: 'extended', - type: 'keyword', - description: 'Primary group name of the file.', - }, - { - name: 'inode', - level: 'extended', - type: 'keyword', - description: 'Inode representing the file in the filesystem.', - }, - { - name: 'mode', - level: 'extended', - type: 'keyword', - description: 'Mode of the file in octal representation.', - example: 416, - }, - { - name: 'mtime', - level: 'extended', - type: 'date', - description: 'Last time file content was modified.', - }, - { - name: 'owner', - level: 'extended', - type: 'keyword', - description: "File owner's username.", - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - description: 'Path to the file.', - }, - { - name: 'size', - level: 'extended', - type: 'long', - description: 'File size in bytes (field is only added when `type` is `file`).', - }, - { - name: 'target_path', - level: 'extended', - type: 'keyword', - description: 'Target path for symlinks.', - }, - { - name: 'type', - level: 'extended', - type: 'keyword', - description: 'File type (file, dir, or symlink).', - }, - { - name: 'uid', - level: 'extended', - type: 'keyword', - description: 'The user ID (UID) or security identifier (SID) of the file owner.', - }, - ], - }, - { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an\nevent.\n\nThis geolocation information can be derived from techniques such as Geo IP,\nor be user-supplied.', - type: 'group', - fields: [ - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - ], - }, - { - name: 'group', - title: 'Group', - group: 2, - description: - 'The group fields are meant to represent groups that are relevant\nto the event.', - type: 'group', - fields: [ - { - name: 'id', - level: 'extended', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - description: 'Name of the group.', - }, - ], - }, - { - name: 'host', - title: 'Host', - group: 2, - description: - 'A host is defined as a general computing instance.\n\nECS host.* fields should be populated with details about the host on which the\nevent happened, or from which the measurement was taken. Host types include\nhardware, virtual machines, Docker containers, and Kubernetes nodes.', - type: 'group', - fields: [ - { - name: 'architecture', - level: 'core', - type: 'keyword', - description: 'Operating system architecture.', - example: 'x86_64', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'hostname', - level: 'core', - type: 'keyword', - description: - 'Hostname of the host.\n\nIt normally contains what the `hostname` command returns on the host machine.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - description: - 'Unique host id.\n\nAs hostname is not always unique, use values that are meaningful in your environment.\n\nExample: The current usage of `beat.name`.', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'Host ip address.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - description: 'Host mac address.', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: - 'Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - description: - 'Type of host.\n\nFor Cloud providers this can be the machine type like `t2.medium`. If vm,\nthis could be the container, for example, or other information meaningful\nin your environment.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'http', - title: 'HTTP', - group: 2, - description: - 'Fields related to HTTP activity. Use the `url` field set to store\nthe url of the request.', - type: 'group', - fields: [ - { - name: 'request.body.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the request body.', - example: 887, - }, - { - name: 'request.body.content', - level: 'extended', - type: 'keyword', - description: 'The full HTTP request body.', - example: 'Hello world', - }, - { - name: 'request.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the request (body and headers).', - example: 1437, - }, - { - name: 'request.method', - level: 'extended', - type: 'keyword', - description: - 'HTTP request method.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'get, post, put', - }, - { - name: 'request.referrer', - level: 'extended', - type: 'keyword', - description: 'Referrer for this HTTP request.', - example: 'https://blog.example.com/', - }, - { - name: 'response.body.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the response body.', - example: 887, - }, - { - name: 'response.body.content', - level: 'extended', - type: 'keyword', - description: 'The full HTTP response body.', - example: 'Hello world', - }, - { - name: 'response.bytes', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the response (body and headers).', - example: 1437, - }, - { - name: 'response.status_code', - level: 'extended', - type: 'long', - format: 'string', - description: 'HTTP response status code.', - example: 404, - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - description: 'HTTP version.', - example: 1.1, - }, - ], - }, - { - name: 'log', - title: 'Log', - group: 2, - description: 'Fields which are specific to log events.', - type: 'group', - fields: [ - { - name: 'level', - level: 'core', - type: 'keyword', - description: - 'Original log level of the log event.\n\nSome examples are `warn`, `error`, `i`.', - example: 'err', - }, - { - name: 'original', - level: 'core', - type: 'keyword', - description: - 'This is the original log message and contains the full log message\nbefore splitting it up in multiple parts.\n\nIn contrast to the `message` field which can contain an extracted part of\nthe log message, this field contains the original, full log message. It can\nhave already some modifications applied like encoding or new lines removed\nto clean up the log message.\n\nThis field is not indexed and doc_values are disabled so it cant be queried\nbut the value can be retrieved from `_source`.', - example: 'Sep 19 08:26:10 localhost My log', - }, - ], - }, - { - name: 'network', - title: 'Network', - group: 2, - description: - 'The network is defined as the communication path over which a host\nor network event happens.\n\nThe network.* fields should be populated with details about the network activity\nassociated with an event.', - type: 'group', - fields: [ - { - name: 'application', - level: 'extended', - type: 'keyword', - description: - 'A name given to an application level protocol. This can be arbitrarily\nassigned for things like microservices, but also apply to things like skype,\nicq, facebook, twitter. This would be used in situations where the vendor\nor service can be decoded such as from the source/dest IP owners, ports, or\nwire format.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'aim', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: - 'Total bytes transferred in both directions.\n\nIf `source.bytes` and `destination.bytes` are known, `network.bytes` is their\nsum.', - example: 368, - }, - { - name: 'community_id', - level: 'extended', - type: 'keyword', - description: - 'A hash of source and destination IPs and ports, as well as the\nprotocol used in a communication. This is a tool-agnostic standard to identify\nflows.\n\nLearn more at https://github.com/corelight/community-id-spec.', - example: '1:hO+sN4H+MG5MY/8hIrXPqc4ZQz0=', - }, - { - name: 'direction', - level: 'core', - type: 'keyword', - description: - "Direction of the network traffic. Recommended values are: * inbound * outbound * internal * external * unknown When mapping events from a host-based monitoring context, populate this field from the host's point of view. When mapping events from a network or perimeter-based monitoring context, populate this field from the point of view of your network perimeter.", - example: 'inbound', - }, - { - name: 'forwarded_ip', - level: 'core', - type: 'ip', - description: 'Host IP address when the source IP address is the proxy.', - example: '192.1.1.2', - }, - { - name: 'iana_number', - level: 'extended', - type: 'keyword', - description: - 'IANA Protocol Number (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml).\nStandardized list of protocols. This aligns well with NetFlow and sFlow related\nlogs which use the IANA Protocol Number.', - example: 6, - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - description: 'Name given by operators to sections of their network.', - example: 'Guest Wifi', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: - 'Total packets transferred in both directions.\n\nIf `source.packets` and `destination.packets` are known, `network.packets`\nis their sum.', - example: 24, - }, - { - name: 'protocol', - level: 'core', - type: 'keyword', - description: - 'L7 Network protocol name. ex. http, lumberjack, transport protocol.\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'http', - }, - { - name: 'transport', - level: 'core', - type: 'keyword', - description: - 'Same as network.iana_number, but instead using the Keyword name\nof the transport layer (udp, tcp, ipv6-icmp, etc.)\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'tcp', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - description: - 'In the OSI Model this would be the Network Layer. ipv4, ipv6,\nipsec, pim, etc\n\nThe field value must be normalized to lowercase for querying. See the documentation\nsection "Implementing ECS".', - example: 'ipv4', - }, - ], - }, - { - name: 'observer', - title: 'Observer', - group: 2, - description: - 'An observer is defined as a special network, security, or application\ndevice used to detect, observe, or create network, security, or application-related\nevents and metrics.\n\nThis could be a custom hardware appliance or a server that has been configured\nto run special network, security, or application software. Examples include\nfirewalls, intrusion detection/prevention systems, network monitoring sensors,\nweb application firewalls, data loss prevention systems, and APM servers. The\nobserver.* fields shall be populated with details of the system, if any, that\ndetects, observes and/or creates a network, security, or application event or\nmetric. Message queues and ETL components used in processing events or metrics\nare not considered observers in ECS.', - type: 'group', - fields: [ - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'hostname', - level: 'core', - type: 'keyword', - description: 'Hostname of the observer.', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'IP address of the observer.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - description: 'MAC address of the observer', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'serial_number', - level: 'extended', - type: 'keyword', - description: 'Observer serial number.', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - description: - 'The type of the observer the data is coming from.\n\nThere is no predefined list of observer types. Some examples are `forwarder`,\n`firewall`, `ids`, `ips`, `proxy`, `poller`, `sensor`, `APM server`.', - example: 'firewall', - }, - { - name: 'vendor', - level: 'core', - type: 'keyword', - description: 'observer vendor information.', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - description: 'Observer version.', - }, - ], - }, - { - name: 'organization', - title: 'Organization', - group: 2, - description: - 'The organization fields enrich data with information about the company\nor entity the data is associated with.\n\nThese fields help you arrange or filter data stored in an index by one or multiple\norganizations.', - type: 'group', - fields: [ - { - name: 'id', - level: 'extended', - type: 'keyword', - description: 'Unique identifier for the organization.', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - description: 'Organization name.', - }, - ], - }, - { - name: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - type: 'group', - fields: [ - { - name: 'family', - level: 'extended', - type: 'keyword', - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - ], - }, - { - name: 'process', - title: 'Process', - group: 2, - description: - 'These fields contain information about a process.\n\nThese fields can help you correlate metrics information with a process id/name\nfrom a log message. The `process.pid` often stays in the metric itself and\nis copied to the global field for correlation.', - type: 'group', - fields: [ - { - name: 'args', - level: 'extended', - type: 'keyword', - description: - 'Array of process arguments.\n\nMay be filtered to protect sensitive information.', - example: ['ssh', '-l', 'user', '10.0.0.16'], - }, - { - name: 'executable', - level: 'extended', - type: 'keyword', - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - description: 'Process name.\n\nSometimes called program name or similar.', - example: 'ssh', - }, - { - name: 'pid', - level: 'core', - type: 'long', - format: 'string', - description: 'Process id.', - example: 4242, - }, - { - name: 'ppid', - level: 'extended', - type: 'long', - format: 'string', - description: "Parent process' pid.", - example: 4241, - }, - { - name: 'start', - level: 'extended', - type: 'date', - description: 'The time the process started.', - example: '2016-05-23T08:05:34.853Z', - }, - { - name: 'thread.id', - level: 'extended', - type: 'long', - format: 'string', - description: 'Thread ID.', - example: 4242, - }, - { - name: 'title', - level: 'extended', - type: 'keyword', - description: - 'Process title.\n\nThe proctitle, some times the same as process name. Can also be different:\nfor example a browser setting its title to the web page currently opened.', - }, - { - name: 'working_directory', - level: 'extended', - type: 'keyword', - description: 'The working directory of the process.', - example: '/home/alice', - }, - ], - }, - { - name: 'related', - title: 'Related', - group: 2, - description: - 'This field set is meant to facilitate pivoting around a piece of\ndata.\n\nSome pieces of information can be seen in many places in an ECS event. To facilitate\nsearching for them, store an array of all seen values to their corresponding\nfield in `related.`.\n\nA concrete example is IP addresses, which can be under host, observer, source,\ndestination, client, server, and network.forwarded_ip. If you append all IPs\nto `related.ip`, you can then search for a given IP trivially, no matter where\nit appeared, by querying `related.ip:a.b.c.d`.', - type: 'group', - fields: [ - { - name: 'ip', - level: 'extended', - type: 'ip', - description: 'All of the IPs seen on your event.', - }, - ], - }, - { - name: 'server', - title: 'Server', - group: 2, - description: - 'A Server is defined as the responder in a network connection for\nevents regarding sessions, connections, or bidirectional flow records.\n\nFor TCP events, the server is the receiver of the initial SYN packet(s) of the\nTCP connection. For other protocols, the server is generally the responder in\nthe network transaction. Some systems actually use the term "responder" to refer\nthe server in TCP connections. The server fields describe details about the\nsystem acting as the server in the network event. Server fields are usually\npopulated in conjunction with client fields. Server fields are generally not\npopulated for packet-level events.\n\nClient / server representations can add semantic context to an exchange, which\nis helpful to visualize the data in certain situations. If your context falls\nin that category, you should still ensure that source and destination are filled\nappropriately.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - description: - 'Some event server addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the server to the client.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - description: 'Server domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the server.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - description: 'MAC address of the server.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the server to the client.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the server.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'service', - title: 'Service', - group: 2, - description: - 'The service fields describe the service for or from which the data\nwas collected.\n\nThese fields help you find and correlate logs for a specific service and version.', - type: 'group', - fields: [ - { - name: 'ephemeral_id', - level: 'extended', - type: 'keyword', - description: - 'Ephemeral identifier of this service (if one exists).\n\nThis id normally changes across restarts, but `service.id` does not.', - example: '8a4f500f', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - description: - 'Unique identifier of the running service.\n\nThis id should uniquely identify this service. This makes it possible to correlate\nlogs and metrics for one specific service.\n\nExample: If you are experiencing issues with one redis instance, you can filter\non that id to see metrics and logs for that single instance.', - example: 'd37e5ebfe0ae6c4972dbe9f0174a1637bb8247f6', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: - 'Name of the service data is collected from.\n\nThe name of the service is normally user given. This allows if two instances\nof the same service are running on the same machine they can be differentiated\nby the `service.name`.\n\nAlso it allows for distributed services that run on multiple hosts to correlate\nthe related instances based on the name.\n\nIn the case of Elasticsearch the service.name could contain the cluster name.\nFor Beats the service.name is by default a copy of the `service.type` field\nif no name is specified.', - example: 'elasticsearch-metrics', - }, - { - name: 'state', - level: 'core', - type: 'keyword', - description: 'Current state of the service.', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - description: - 'The type of the service data is collected from.\n\nThe type can be used to group and correlate logs and metrics from one service\ntype.\n\nExample: If logs or metrics are collected from Elasticsearch, `service.type`\nwould be `elasticsearch`.', - example: 'elasticsearch', - }, - { - name: 'version', - level: 'core', - type: 'keyword', - description: - 'Version of the service the data was collected from.\n\nThis allows to look at a data set only for a specific version of a service.', - example: '3.2.4', - }, - ], - }, - { - name: 'source', - title: 'Source', - group: 2, - description: - 'Source fields describe details about the source of a packet/event.\n\nSource fields are usually populated in conjunction with destination fields.', - type: 'group', - fields: [ - { - name: 'address', - level: 'extended', - type: 'keyword', - description: - 'Some event source addresses are defined ambiguously. The event\nwill sometimes list an IP, a domain or a unix socket. You should always store\nthe raw address in the `.address` field.\n\nThen it should be duplicated to `.ip` or `.domain`, depending on which one\nit is.', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: 'Bytes sent from the source to the destination.', - example: 184, - }, - { - name: 'domain', - level: 'core', - type: 'keyword', - description: 'Source domain.', - }, - { - name: 'geo.city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'geo.continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'geo.country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'geo.country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'geo.location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'geo.name', - level: 'extended', - type: 'keyword', - description: - 'User-defined description of a location, at the level of granularity\nthey care about.\n\nCould be the name of their data centers, the floor number, if this describes\na local physical entity, city names.\n\nNot typically used in automated geolocation.', - example: 'boston-dc', - }, - { - name: 'geo.region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'geo.region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the source.\n\nCan be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - description: 'MAC address of the source.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: 'Packets sent from the source to the destination.', - example: 12, - }, - { - name: 'port', - level: 'core', - type: 'long', - format: 'string', - description: 'Port of the source.', - }, - { - name: 'user.email', - level: 'extended', - type: 'keyword', - description: 'User email address.', - }, - { - name: 'user.full_name', - level: 'extended', - type: 'keyword', - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'user.group.id', - level: 'extended', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'user.group.name', - level: 'extended', - type: 'keyword', - description: 'Name of the group.', - }, - { - name: 'user.hash', - level: 'extended', - type: 'keyword', - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'user.id', - level: 'core', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.', - }, - { - name: 'user.name', - level: 'core', - type: 'keyword', - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'url', - title: 'URL', - group: 2, - description: - 'URL fields provide support for complete or partial URLs, and supports\nthe breaking down into scheme, domain, path, and so on.', - type: 'group', - fields: [ - { - name: 'domain', - level: 'extended', - type: 'keyword', - description: - 'Domain of the url, such as "www.elastic.co".\n\nIn some cases a URL may refer to an IP and/or port directly, without a domain\nname. In this case, the IP address would go to the `domain` field.', - example: 'www.elastic.co', - }, - { - name: 'fragment', - level: 'extended', - type: 'keyword', - description: - 'Portion of the url after the `#`, such as "top".\n\nThe `#` is not part of the fragment.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - description: - 'If full URLs are important to your use case, they should be stored\nin `url.full`, whether this field is reconstructed or present in the event\nsource.', - example: 'https://www.elastic.co:443/search?q=elasticsearch#top', - }, - { - name: 'original', - level: 'extended', - type: 'keyword', - description: - 'Unmodified original url as seen in the event source.\n\nNote that in network monitoring, the observed URL may be a full URL, whereas\nin access logs, the URL is often just represented as a path.\n\nThis field is meant to represent the URL as it was observed, complete or not.', - example: - 'https://www.elastic.co:443/search?q=elasticsearch#top or /search?q=elasticsearch', - }, - { - name: 'password', - level: 'extended', - type: 'keyword', - description: 'Password of the request.', - }, - { - name: 'path', - level: 'extended', - type: 'keyword', - description: 'Path of the request, such as "/search".', - }, - { - name: 'port', - level: 'extended', - type: 'long', - format: 'string', - description: 'Port of the request, such as 443.', - example: 443, - }, - { - name: 'query', - level: 'extended', - type: 'keyword', - description: - 'The query field describes the query string of the request, such\nas "q=elasticsearch".\n\nThe `?` is excluded from the query string. If a URL contains no `?`, there\nis no query field. If there is a `?` but no query, the query field exists\nwith an empty string. The `exists` query can be used to differentiate between\nthe two cases.', - }, - { - name: 'scheme', - level: 'extended', - type: 'keyword', - description: - 'Scheme of the request, such as "https".\n\nNote: The `:` is not part of the scheme.', - example: 'https', - }, - { - name: 'username', - level: 'extended', - type: 'keyword', - description: 'Username of the request.', - }, - ], - }, - { - name: 'user', - title: 'User', - group: 2, - description: - 'The user fields describe information about the user that is relevant\nto the event.\n\nFields can have one entry or multiple entries. If a user has more than one id,\nprovide an array that includes all of them.', - type: 'group', - fields: [ - { - name: 'email', - level: 'extended', - type: 'keyword', - description: 'User email address.', - }, - { - name: 'full_name', - level: 'extended', - type: 'keyword', - description: "User's full name, if available.", - example: 'Albert Einstein', - }, - { - name: 'group.id', - level: 'extended', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'group.name', - level: 'extended', - type: 'keyword', - description: 'Name of the group.', - }, - { - name: 'hash', - level: 'extended', - type: 'keyword', - description: - 'Unique user hash to correlate information for a user in anonymized\nform.\n\nUseful if `user.id` or `user.name` contain confidential information and cannot\nbe used.', - }, - { - name: 'id', - level: 'core', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.', - }, - { - name: 'name', - level: 'core', - type: 'keyword', - description: 'Short name or login of the user.', - example: 'albert', - }, - ], - }, - { - name: 'user_agent', - title: 'User agent', - group: 2, - description: - 'The user_agent fields normally come from a browser request.\n\nThey often show up in web service logs coming from the parsed user agent string.', - type: 'group', - fields: [ - { - name: 'device.name', - level: 'extended', - type: 'keyword', - description: 'Name of the device.', - example: 'iPhone', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - description: 'Name of the user agent.', - example: 'Safari', - }, - { - name: 'original', - level: 'extended', - type: 'keyword', - description: 'Unparsed version of the user_agent.', - example: - 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15\n(KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1', - }, - { - name: 'os.family', - level: 'extended', - type: 'keyword', - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - }, - { - name: 'os.full', - level: 'extended', - type: 'keyword', - description: 'Operating system name, including the version or code name.', - example: 'Mac OS Mojave', - }, - { - name: 'os.kernel', - level: 'extended', - type: 'keyword', - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - }, - { - name: 'os.name', - level: 'extended', - type: 'keyword', - description: 'Operating system name, without the version.', - example: 'Mac OS X', - }, - { - name: 'os.platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'os.version', - level: 'extended', - type: 'keyword', - description: 'Operating system version as a raw string.', - example: '10.14.1', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - description: 'Version of the user agent.', - example: 12, - }, - ], - }, - ], - }, - { - key: 'beat', - title: 'Beat', - description: 'Contains common beat fields available in all event types.\n', - fields: [ - { - name: 'agent.hostname', - type: 'keyword', - description: 'Hostname of the agent.', - }, - { - name: 'beat.timezone', - type: 'alias', - path: 'event.timezone', - migration: true, - }, - { - name: 'fields', - type: 'object', - object_type: 'keyword', - description: 'Contains user configurable fields.\n', - }, - { - name: 'error', - type: 'group', - description: 'Error fields containing additional info in case of errors.\n', - fields: [ - { - name: 'type', - type: 'keyword', - description: 'Error type.\n', - }, - ], - }, - { - name: 'beat.name', - type: 'alias', - path: 'host.name', - migration: true, - }, - { - name: 'beat.hostname', - type: 'alias', - path: 'agent.hostname', - migration: true, - }, - { - name: 'timeseries.instance', - type: 'keyword', - description: 'Time series instance id', - }, - ], - }, - { - key: 'cloud', - title: 'Cloud provider metadata', - description: 'Metadata from cloud providers added by the add_cloud_metadata processor.\n', - fields: [ - { - name: 'cloud.project.id', - example: 'project-x', - description: 'Name of the project in Google Cloud.\n', - }, - { - name: 'meta.cloud.provider', - type: 'alias', - path: 'cloud.provider', - migration: true, - }, - { - name: 'meta.cloud.instance_id', - type: 'alias', - path: 'cloud.instance.id', - migration: true, - }, - { - name: 'meta.cloud.instance_name', - type: 'alias', - path: 'cloud.instance.name', - migration: true, - }, - { - name: 'meta.cloud.machine_type', - type: 'alias', - path: 'cloud.machine.type', - migration: true, - }, - { - name: 'meta.cloud.availability_zone', - type: 'alias', - path: 'cloud.availability_zone', - migration: true, - }, - { - name: 'meta.cloud.project_id', - type: 'alias', - path: 'cloud.project.id', - migration: true, - }, - { - name: 'meta.cloud.region', - type: 'alias', - path: 'cloud.region', - migration: true, - }, - ], - }, - { - key: 'docker', - title: 'Docker', - description: 'Docker stats collected from Docker.\n', - short_config: false, - anchor: 'docker-processor', - fields: [ - { - name: 'docker', - type: 'group', - fields: [ - { - name: 'container.id', - type: 'alias', - path: 'container.id', - migration: true, - }, - { - name: 'container.image', - type: 'alias', - path: 'container.image.name', - migration: true, - }, - { - name: 'container.name', - type: 'alias', - path: 'container.name', - migration: true, - }, - { - name: 'container.labels', - type: 'object', - object_type: 'keyword', - description: 'Image labels.\n', - }, - ], - }, - ], - }, - { - key: 'host', - title: 'Host', - description: 'Info collected for the host machine.\n', - anchor: 'host-processor', - fields: [ - { - name: 'host', - type: 'group', - fields: [ - { - name: 'containerized', - type: 'boolean', - description: 'If the host is a container.\n', - }, - { - name: 'os.build', - type: 'keyword', - example: '18D109', - description: 'OS build information.\n', - }, - { - name: 'os.codename', - type: 'keyword', - example: 'stretch', - description: 'OS codename, if any.\n', - }, - ], - }, - ], - }, - { - key: 'kubernetes', - title: 'Kubernetes', - description: 'Kubernetes metadata added by the kubernetes processor\n', - short_config: false, - anchor: 'kubernetes-processor', - fields: [ - { - name: 'kubernetes', - type: 'group', - fields: [ - { - name: 'pod.name', - type: 'keyword', - description: 'Kubernetes pod name\n', - }, - { - name: 'pod.uid', - type: 'keyword', - description: 'Kubernetes Pod UID\n', - }, - { - name: 'namespace', - type: 'keyword', - description: 'Kubernetes namespace\n', - }, - { - name: 'node.name', - type: 'keyword', - description: 'Kubernetes node name\n', - }, - { - name: 'labels', - type: 'object', - description: 'Kubernetes labels map\n', - }, - { - name: 'annotations', - type: 'object', - description: 'Kubernetes annotations map\n', - }, - { - name: 'replicaset.name', - type: 'keyword', - description: 'Kubernetes replicaset name\n', - }, - { - name: 'deployment.name', - type: 'keyword', - description: 'Kubernetes deployment name\n', - }, - { - name: 'statefulset.name', - type: 'keyword', - description: 'Kubernetes statefulset name\n', - }, - { - name: 'container.name', - type: 'keyword', - description: 'Kubernetes container name\n', - }, - { - name: 'container.image', - type: 'keyword', - description: 'Kubernetes container image\n', - }, - ], - }, - ], - }, - { - key: 'process', - title: 'Process', - description: 'Process metadata fields\n', - fields: [ - { - name: 'process', - type: 'group', - fields: [ - { - name: 'exe', - type: 'alias', - path: 'process.executable', - migration: true, - }, - ], - }, - ], - }, - { - key: 'jolokia-autodiscover', - title: 'Jolokia Discovery autodiscover provider', - description: 'Metadata from Jolokia Discovery added by the jolokia provider.\n', - fields: [ - { - name: 'jolokia.agent.version', - type: 'keyword', - description: 'Version number of jolokia agent.\n', - }, - { - name: 'jolokia.agent.id', - type: 'keyword', - description: - 'Each agent has a unique id which can be either provided during startup of the agent in form of a configuration parameter or being autodetected. If autodected, the id has several parts: The IP, the process id, hashcode of the agent and its type.\n', - }, - { - name: 'jolokia.server.product', - type: 'keyword', - description: 'The container product if detected.\n', - }, - { - name: 'jolokia.server.version', - type: 'keyword', - description: "The container's version (if detected).\n", - }, - { - name: 'jolokia.server.vendor', - type: 'keyword', - description: 'The vendor of the container the agent is running in.\n', - }, - { - name: 'jolokia.url', - type: 'keyword', - description: 'The URL how this agent can be contacted.\n', - }, - { - name: 'jolokia.secured', - type: 'boolean', - description: 'Whether the agent was configured for authentication or not.\n', - }, - ], - }, - { - key: 'winlog', - title: 'Windows Event Log fields emitted by Winlogbeat', - description: 'Fields from the Windows Event Log.\n', - fields: [ - { - name: 'log.file.path', - type: 'keyword', - required: false, - description: - 'The name of the file the event was read from when Winlogbeat is reading directly from an .evtx file.\n', - }, - { - name: 'event.code', - type: 'keyword', - required: false, - description: 'The code for this log message (Windows event ID).\n', - }, - { - name: 'event.original', - description: - 'The raw XML representation of the event obtained from Windows. This field is only available on operating systems supporting the Windows Event Log API (Microsoft Windows Vista and newer). This field is not included by default and must be enabled by setting `include_xml: true` as a configuration option for an individual event log.\nThe XML representation of the event is useful for troubleshooting purposes. The data in the fields reported by Winlogbeat can be compared to the data in the XML to diagnose problems.\n', - }, - { - name: 'winlog', - type: 'group', - description: 'All fields specific to the Windows Event Log are defined here.\n', - fields: [ - { - name: 'api', - required: true, - description: - 'The event log API type used to read the record. The possible values are "wineventlog" for the Windows Event Log API or "eventlogging" for the Event Logging API.\nThe Event Logging API was designed for Windows Server 2003 or Windows 2000 operating systems. In Windows Vista, the event logging infrastructure was redesigned. On Windows Vista or later operating systems, the Windows Event Log API is used. Winlogbeat automatically detects which API to use for reading event logs.\n', - }, - { - name: 'activity_id', - type: 'keyword', - required: false, - description: - 'A globally unique identifier that identifies the current activity. The events that are published with this identifier are part of the same activity.\n', - }, - { - name: 'computer_name', - type: 'keyword', - required: true, - description: - 'The name of the computer that generated the record. When using Windows event forwarding, this name can differ from `agent.hostname`.\n', - }, - { - name: 'event_data', - type: 'object', - object_type: 'keyword', - required: false, - description: - 'The event-specific data. This field is mutually exclusive with `user_data`. If you are capturing event data on versions prior to Windows Vista, the parameters in `event_data` are named `param1`, `param2`, and so on, because event log parameters are unnamed in earlier versions of Windows.\n', - }, - { - name: 'event_id', - type: 'keyword', - required: true, - description: - 'The event identifier. The value is specific to the source of the event.\n', - }, - { - name: 'keywords', - type: 'keyword', - required: false, - description: 'The keywords are used to classify an event.\n', - }, - { - name: 'channel', - type: 'keyword', - required: true, - description: - 'The name of the channel from which this record was read. This value is one of the names from the `event_logs` collection in the configuration.\n', - }, - { - name: 'record_id', - type: 'keyword', - required: true, - description: - 'The record ID of the event log record. The first record written to an event log is record number 1, and other records are numbered sequentially. If the record number reaches the maximum value (2^32^ for the Event Logging API and 2^64^ for the Windows Event Log API), the next record number will be 0.\n', - }, - { - name: 'related_activity_id', - type: 'keyword', - required: false, - description: - 'A globally unique identifier that identifies the activity to which control was transferred to. The related events would then have this identifier as their `activity_id` identifier.\n', - }, - { - name: 'opcode', - type: 'keyword', - required: false, - description: - 'The opcode defined in the event. Task and opcode are typically used to identify the location in the application from where the event was logged.\n', - }, - { - name: 'provider_guid', - type: 'keyword', - required: false, - description: - 'A globally unique identifier that identifies the provider that logged the event.\n', - }, - { - name: 'process.pid', - type: 'long', - required: false, - description: 'The process_id of the Client Server Runtime Process.\n', - }, - { - name: 'provider_name', - type: 'keyword', - required: true, - description: - 'The source of the event log record (the application or service that logged the record).\n', - }, - { - name: 'task', - type: 'keyword', - required: false, - description: - 'The task defined in the event. Task and opcode are typically used to identify the location in the application from where the event was logged. The category used by the Event Logging API (on pre Windows Vista operating systems) is written to this field.\n', - }, - { - name: 'process.thread.id', - type: 'long', - required: false, - }, - { - name: 'user_data', - type: 'object', - object_type: 'keyword', - required: false, - description: - 'The event specific data. This field is mutually exclusive with `event_data`.\n', - }, - { - name: 'user.identifier', - type: 'keyword', - required: false, - example: 'S-1-5-21-3541430928-2051711210-1391384369-1001', - description: - 'The Windows security identifier (SID) of the account associated with this event.\n\nIf Winlogbeat cannot resolve the SID to a name, then the `user.name`, `user.domain`, and `user.type` fields will be omitted from the event. If you discover Winlogbeat not resolving SIDs, review the log for clues as to what the problem may be.\n', - }, - { - name: 'user.domain', - type: 'keyword', - required: false, - description: 'The domain that the account associated with this event is a member of.\n', - }, - { - name: 'user.type', - type: 'keyword', - required: false, - description: 'The type of account associated with this event.\n', - }, - { - name: 'version', - type: 'long', - required: false, - description: "The version number of the event's definition.", - }, - ], - }, - ], - }, - { - key: 'eventlog', - title: 'Event log record', - description: 'Contains data from a Windows event log record.\n', - fields: [ - { - name: 'type', - type: 'alias', - path: 'winlog.api', - migration: true, - }, - { - name: 'activity_id', - type: 'alias', - path: 'winlog.activity_id', - migration: true, - }, - { - name: 'computer_name', - type: 'alias', - path: 'winlog.computer_name', - migration: true, - }, - { - name: 'event_id', - type: 'alias', - path: 'winlog.event_id', - migration: true, - }, - { - name: 'keywords', - type: 'alias', - path: 'winlog.keywords', - migration: true, - }, - { - name: 'log_name', - type: 'alias', - path: 'winlog.channel', - migration: true, - }, - { - name: 'message_error', - type: 'alias', - path: 'error.message', - migration: true, - }, - { - name: 'record_number', - type: 'alias', - path: 'winlog.record_id', - migration: true, - }, - { - name: 'related_activity_id', - type: 'alias', - path: 'winlog.related_activity_id', - migration: true, - }, - { - name: 'opcode', - type: 'alias', - path: 'winlog.opcode', - migration: true, - }, - { - name: 'provider_guid', - type: 'alias', - path: 'winlog.provider_guid', - migration: true, - }, - { - name: 'process_id', - type: 'alias', - path: 'winlog.process.pid', - migration: true, - }, - { - name: 'source_name', - type: 'alias', - path: 'winlog.provider_name', - migration: true, - }, - { - name: 'task', - type: 'alias', - path: 'winlog.task', - migration: true, - }, - { - name: 'thread_id', - type: 'alias', - path: 'winlog.process.thread.id', - migration: true, - }, - { - name: 'user.identifier', - type: 'alias', - path: 'winlog.user.identifier', - migration: true, - }, - { - name: 'user.domain', - type: 'alias', - path: 'winlog.user.domain', - migration: true, - }, - { - name: 'user.type', - type: 'alias', - path: 'winlog.user.type', - migration: true, - }, - { - name: 'version', - type: 'alias', - path: 'winlog.version', - migration: true, - }, - { - name: 'xml', - type: 'alias', - path: 'event.original', - migration: true, - }, - ], - }, -]; diff --git a/x-pack/plugins/security_solution/server/utils/beat_schema/fields.ts b/x-pack/plugins/security_solution/server/utils/beat_schema/fields.ts new file mode 100644 index 00000000000000..e61b9ae008a623 --- /dev/null +++ b/x-pack/plugins/security_solution/server/utils/beat_schema/fields.ts @@ -0,0 +1,36118 @@ +/* + * 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 { BeatFields } from '../../../common/search_strategy/index_fields'; + +/* eslint-disable @typescript-eslint/naming-convention */ +export const fieldsBeat: BeatFields = { + _id: { + category: 'base', + description: 'Each document has an _id that uniquely identifies it', + example: 'Y-6TfmcB0WOhS6qyMv3s', + name: '_id', + type: 'keyword', + }, + _index: { + category: 'base', + description: + 'An index is like a ‘database’ in a relational database. It has a mapping which defines multiple types. An index is a logical namespace which maps to one or more primary shards and can have zero or more replica shards.', + example: 'auditbeat-8.0.0-2019.02.19-000001', + name: '_index', + type: 'keyword', + }, + '@timestamp': { + category: 'base', + description: + 'Date/time when the event originated. This is the date/time extracted from the event, typically representing when the event was generated by the source. If the event source has no original timestamp, this value is typically populated by the first time the event was received by the pipeline. Required field for all events.', + example: '2016-05-23T08:05:34.853Z', + name: '@timestamp', + type: 'date', + }, + labels: { + category: 'base', + description: + 'Custom key/value pairs. Can be used to add meta information to events. Should not contain nested objects. All values are stored as keyword. Example: `docker` and `k8s` labels.', + example: '{"application": "foo-bar", "env": "production"}', + name: 'labels', + type: 'object', + }, + message: { + category: 'base', + description: + 'For log events the message field contains the log message, optimized for viewing in a log viewer. For structured logs without an original message field, other fields can be concatenated to form a human-readable summary of the event. If multiple messages exist, they can be combined into one message.', + example: 'Hello World', + name: 'message', + type: 'text', + }, + tags: { + category: 'base', + description: 'List of keywords used to tag each event.', + example: '["production", "env2"]', + name: 'tags', + type: 'keyword', + }, + 'agent.ephemeral_id': { + category: 'agent', + description: + 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', + example: '8a4f500f', + name: 'agent.ephemeral_id', + type: 'keyword', + }, + 'agent.id': { + category: 'agent', + description: + 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', + example: '8a4f500d', + name: 'agent.id', + type: 'keyword', + }, + 'agent.name': { + category: 'agent', + description: + 'Custom name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', + example: 'foo', + name: 'agent.name', + type: 'keyword', + }, + 'agent.type': { + category: 'agent', + description: + 'Type of the agent. The agent type stays always the same and should be given by the agent used. In case of Filebeat the agent would always be Filebeat also if two Filebeat instances are run on the same machine.', + example: 'filebeat', + name: 'agent.type', + type: 'keyword', + }, + 'agent.version': { + category: 'agent', + description: 'Version of the agent.', + example: '6.0.0-rc2', + name: 'agent.version', + type: 'keyword', + }, + 'as.number': { + category: 'as', + description: + 'Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet.', + example: 15169, + name: 'as.number', + type: 'long', + }, + 'as.organization.name': { + category: 'as', + description: 'Organization name.', + example: 'Google LLC', + name: 'as.organization.name', + type: 'keyword', + }, + 'client.address': { + category: 'client', + description: + 'Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', + name: 'client.address', + type: 'keyword', + }, + 'client.as.number': { + category: 'client', + description: + 'Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet.', + example: 15169, + name: 'client.as.number', + type: 'long', + }, + 'client.as.organization.name': { + category: 'client', + description: 'Organization name.', + example: 'Google LLC', + name: 'client.as.organization.name', + type: 'keyword', + }, + 'client.bytes': { + category: 'client', + description: 'Bytes sent from the client to the server.', + example: 184, + name: 'client.bytes', + type: 'long', + format: 'bytes', + }, + 'client.domain': { + category: 'client', + description: 'Client domain.', + name: 'client.domain', + type: 'keyword', + }, + 'client.geo.city_name': { + category: 'client', + description: 'City name.', + example: 'Montreal', + name: 'client.geo.city_name', + type: 'keyword', + }, + 'client.geo.continent_name': { + category: 'client', + description: 'Name of the continent.', + example: 'North America', + name: 'client.geo.continent_name', + type: 'keyword', + }, + 'client.geo.country_iso_code': { + category: 'client', + description: 'Country ISO code.', + example: 'CA', + name: 'client.geo.country_iso_code', + type: 'keyword', + }, + 'client.geo.country_name': { + category: 'client', + description: 'Country name.', + example: 'Canada', + name: 'client.geo.country_name', + type: 'keyword', + }, + 'client.geo.location': { + category: 'client', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', + name: 'client.geo.location', + type: 'geo_point', + }, + 'client.geo.name': { + category: 'client', + description: + 'User-defined description of a location, at the level of granularity they care about. Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. Not typically used in automated geolocation.', + example: 'boston-dc', + name: 'client.geo.name', + type: 'keyword', + }, + 'client.geo.region_iso_code': { + category: 'client', + description: 'Region ISO code.', + example: 'CA-QC', + name: 'client.geo.region_iso_code', + type: 'keyword', + }, + 'client.geo.region_name': { + category: 'client', + description: 'Region name.', + example: 'Quebec', + name: 'client.geo.region_name', + type: 'keyword', + }, + 'client.ip': { + category: 'client', + description: 'IP address of the client. Can be one or multiple IPv4 or IPv6 addresses.', + name: 'client.ip', + type: 'ip', + }, + 'client.mac': { + category: 'client', + description: 'MAC address of the client.', + name: 'client.mac', + type: 'keyword', + }, + 'client.nat.ip': { + category: 'client', + description: + 'Translated IP of source based NAT sessions (e.g. internal client to internet). Typically connections traversing load balancers, firewalls, or routers.', + name: 'client.nat.ip', + type: 'ip', + }, + 'client.nat.port': { + category: 'client', + description: + 'Translated port of source based NAT sessions (e.g. internal client to internet). Typically connections traversing load balancers, firewalls, or routers.', + name: 'client.nat.port', + type: 'long', + format: 'string', + }, + 'client.packets': { + category: 'client', + description: 'Packets sent from the client to the server.', + example: 12, + name: 'client.packets', + type: 'long', + }, + 'client.port': { + category: 'client', + description: 'Port of the client.', + name: 'client.port', + type: 'long', + format: 'string', + }, + 'client.registered_domain': { + category: 'client', + description: + 'The highest registered client domain, stripped of the subdomain. For example, the registered domain for "foo.google.com" is "google.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', + example: 'google.com', + name: 'client.registered_domain', + type: 'keyword', + }, + 'client.top_level_domain': { + category: 'client', + description: + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', + example: 'co.uk', + name: 'client.top_level_domain', + type: 'keyword', + }, + 'client.user.domain': { + category: 'client', + description: + 'Name of the directory the user is a member of. For example, an LDAP or Active Directory domain name.', + name: 'client.user.domain', + type: 'keyword', + }, + 'client.user.email': { + category: 'client', + description: 'User email address.', + name: 'client.user.email', + type: 'keyword', + }, + 'client.user.full_name': { + category: 'client', + description: "User's full name, if available.", + example: 'Albert Einstein', + name: 'client.user.full_name', + type: 'keyword', + }, + 'client.user.group.domain': { + category: 'client', + description: + 'Name of the directory the group is a member of. For example, an LDAP or Active Directory domain name.', + name: 'client.user.group.domain', + type: 'keyword', + }, + 'client.user.group.id': { + category: 'client', + description: 'Unique identifier for the group on the system/platform.', + name: 'client.user.group.id', + type: 'keyword', + }, + 'client.user.group.name': { + category: 'client', + description: 'Name of the group.', + name: 'client.user.group.name', + type: 'keyword', + }, + 'client.user.hash': { + category: 'client', + description: + 'Unique user hash to correlate information for a user in anonymized form. Useful if `user.id` or `user.name` contain confidential information and cannot be used.', + name: 'client.user.hash', + type: 'keyword', + }, + 'client.user.id': { + category: 'client', + description: 'Unique identifiers of the user.', + name: 'client.user.id', + type: 'keyword', + }, + 'client.user.name': { + category: 'client', + description: 'Short name or login of the user.', + example: 'albert', + name: 'client.user.name', + type: 'keyword', + }, + 'cloud.account.id': { + category: 'cloud', + description: + 'The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.', + example: 666777888999, + name: 'cloud.account.id', + type: 'keyword', + }, + 'cloud.availability_zone': { + category: 'cloud', + description: 'Availability zone in which this host is running.', + example: 'us-east-1c', + name: 'cloud.availability_zone', + type: 'keyword', + }, + 'cloud.instance.id': { + category: 'cloud', + description: 'Instance ID of the host machine.', + example: 'i-1234567890abcdef0', + name: 'cloud.instance.id', + type: 'keyword', + }, + 'cloud.instance.name': { + category: 'cloud', + description: 'Instance name of the host machine.', + name: 'cloud.instance.name', + type: 'keyword', + }, + 'cloud.machine.type': { + category: 'cloud', + description: 'Machine type of the host machine.', + example: 't2.medium', + name: 'cloud.machine.type', + type: 'keyword', + }, + 'cloud.provider': { + category: 'cloud', + description: 'Name of the cloud provider. Example values are aws, azure, gcp, or digitalocean.', + example: 'aws', + name: 'cloud.provider', + type: 'keyword', + }, + 'cloud.region': { + category: 'cloud', + description: 'Region in which this host is running.', + example: 'us-east-1', + name: 'cloud.region', + type: 'keyword', + }, + 'code_signature.exists': { + category: 'code_signature', + description: 'Boolean to capture if a signature is present.', + example: 'true', + name: 'code_signature.exists', + type: 'boolean', + }, + 'code_signature.status': { + category: 'code_signature', + description: + 'Additional information about the certificate status. This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked.', + example: 'ERROR_UNTRUSTED_ROOT', + name: 'code_signature.status', + type: 'keyword', + }, + 'code_signature.subject_name': { + category: 'code_signature', + description: 'Subject name of the code signer', + example: 'Microsoft Corporation', + name: 'code_signature.subject_name', + type: 'keyword', + }, + 'code_signature.trusted': { + category: 'code_signature', + description: + 'Stores the trust status of the certificate chain. Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status.', + example: 'true', + name: 'code_signature.trusted', + type: 'boolean', + }, + 'code_signature.valid': { + category: 'code_signature', + description: + 'Boolean to capture if the digital signature is verified against the binary content. Leave unpopulated if a certificate was unchecked.', + example: 'true', + name: 'code_signature.valid', + type: 'boolean', + }, + 'container.id': { + category: 'container', + description: 'Unique container id.', + name: 'container.id', + type: 'keyword', + }, + 'container.image.name': { + category: 'container', + description: 'Name of the image the container was built on.', + name: 'container.image.name', + type: 'keyword', + }, + 'container.image.tag': { + category: 'container', + description: 'Container image tags.', + name: 'container.image.tag', + type: 'keyword', + }, + 'container.labels': { + category: 'container', + description: 'Image labels.', + name: 'container.labels', + type: 'object', + }, + 'container.name': { + category: 'container', + description: 'Container name.', + name: 'container.name', + type: 'keyword', + }, + 'container.runtime': { + category: 'container', + description: 'Runtime managing this container.', + example: 'docker', + name: 'container.runtime', + type: 'keyword', + }, + 'destination.address': { + category: 'destination', + description: + 'Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', + name: 'destination.address', + type: 'keyword', + }, + 'destination.as.number': { + category: 'destination', + description: + 'Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet.', + example: 15169, + name: 'destination.as.number', + type: 'long', + }, + 'destination.as.organization.name': { + category: 'destination', + description: 'Organization name.', + example: 'Google LLC', + name: 'destination.as.organization.name', + type: 'keyword', + }, + 'destination.bytes': { + category: 'destination', + description: 'Bytes sent from the destination to the source.', + example: 184, + name: 'destination.bytes', + type: 'long', + format: 'bytes', + }, + 'destination.domain': { + category: 'destination', + description: 'Destination domain.', + name: 'destination.domain', + type: 'keyword', + }, + 'destination.geo.city_name': { + category: 'destination', + description: 'City name.', + example: 'Montreal', + name: 'destination.geo.city_name', + type: 'keyword', + }, + 'destination.geo.continent_name': { + category: 'destination', + description: 'Name of the continent.', + example: 'North America', + name: 'destination.geo.continent_name', + type: 'keyword', + }, + 'destination.geo.country_iso_code': { + category: 'destination', + description: 'Country ISO code.', + example: 'CA', + name: 'destination.geo.country_iso_code', + type: 'keyword', + }, + 'destination.geo.country_name': { + category: 'destination', + description: 'Country name.', + example: 'Canada', + name: 'destination.geo.country_name', + type: 'keyword', + }, + 'destination.geo.location': { + category: 'destination', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', + name: 'destination.geo.location', + type: 'geo_point', + }, + 'destination.geo.name': { + category: 'destination', + description: + 'User-defined description of a location, at the level of granularity they care about. Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. Not typically used in automated geolocation.', + example: 'boston-dc', + name: 'destination.geo.name', + type: 'keyword', + }, + 'destination.geo.region_iso_code': { + category: 'destination', + description: 'Region ISO code.', + example: 'CA-QC', + name: 'destination.geo.region_iso_code', + type: 'keyword', + }, + 'destination.geo.region_name': { + category: 'destination', + description: 'Region name.', + example: 'Quebec', + name: 'destination.geo.region_name', + type: 'keyword', + }, + 'destination.ip': { + category: 'destination', + description: 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', + name: 'destination.ip', + type: 'ip', + }, + 'destination.mac': { + category: 'destination', + description: 'MAC address of the destination.', + name: 'destination.mac', + type: 'keyword', + }, + 'destination.nat.ip': { + category: 'destination', + description: + 'Translated ip of destination based NAT sessions (e.g. internet to private DMZ) Typically used with load balancers, firewalls, or routers.', + name: 'destination.nat.ip', + type: 'ip', + }, + 'destination.nat.port': { + category: 'destination', + description: + 'Port the source session is translated to by NAT Device. Typically used with load balancers, firewalls, or routers.', + name: 'destination.nat.port', + type: 'long', + format: 'string', + }, + 'destination.packets': { + category: 'destination', + description: 'Packets sent from the destination to the source.', + example: 12, + name: 'destination.packets', + type: 'long', + }, + 'destination.port': { + category: 'destination', + description: 'Port of the destination.', + name: 'destination.port', + type: 'long', + format: 'string', + }, + 'destination.registered_domain': { + category: 'destination', + description: + 'The highest registered destination domain, stripped of the subdomain. For example, the registered domain for "foo.google.com" is "google.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', + example: 'google.com', + name: 'destination.registered_domain', + type: 'keyword', + }, + 'destination.top_level_domain': { + category: 'destination', + description: + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', + example: 'co.uk', + name: 'destination.top_level_domain', + type: 'keyword', + }, + 'destination.user.domain': { + category: 'destination', + description: + 'Name of the directory the user is a member of. For example, an LDAP or Active Directory domain name.', + name: 'destination.user.domain', + type: 'keyword', + }, + 'destination.user.email': { + category: 'destination', + description: 'User email address.', + name: 'destination.user.email', + type: 'keyword', + }, + 'destination.user.full_name': { + category: 'destination', + description: "User's full name, if available.", + example: 'Albert Einstein', + name: 'destination.user.full_name', + type: 'keyword', + }, + 'destination.user.group.domain': { + category: 'destination', + description: + 'Name of the directory the group is a member of. For example, an LDAP or Active Directory domain name.', + name: 'destination.user.group.domain', + type: 'keyword', + }, + 'destination.user.group.id': { + category: 'destination', + description: 'Unique identifier for the group on the system/platform.', + name: 'destination.user.group.id', + type: 'keyword', + }, + 'destination.user.group.name': { + category: 'destination', + description: 'Name of the group.', + name: 'destination.user.group.name', + type: 'keyword', + }, + 'destination.user.hash': { + category: 'destination', + description: + 'Unique user hash to correlate information for a user in anonymized form. Useful if `user.id` or `user.name` contain confidential information and cannot be used.', + name: 'destination.user.hash', + type: 'keyword', + }, + 'destination.user.id': { + category: 'destination', + description: 'Unique identifiers of the user.', + name: 'destination.user.id', + type: 'keyword', + }, + 'destination.user.name': { + category: 'destination', + description: 'Short name or login of the user.', + example: 'albert', + name: 'destination.user.name', + type: 'keyword', + }, + 'dll.code_signature.exists': { + category: 'dll', + description: 'Boolean to capture if a signature is present.', + example: 'true', + name: 'dll.code_signature.exists', + type: 'boolean', + }, + 'dll.code_signature.status': { + category: 'dll', + description: + 'Additional information about the certificate status. This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked.', + example: 'ERROR_UNTRUSTED_ROOT', + name: 'dll.code_signature.status', + type: 'keyword', + }, + 'dll.code_signature.subject_name': { + category: 'dll', + description: 'Subject name of the code signer', + example: 'Microsoft Corporation', + name: 'dll.code_signature.subject_name', + type: 'keyword', + }, + 'dll.code_signature.trusted': { + category: 'dll', + description: + 'Stores the trust status of the certificate chain. Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status.', + example: 'true', + name: 'dll.code_signature.trusted', + type: 'boolean', + }, + 'dll.code_signature.valid': { + category: 'dll', + description: + 'Boolean to capture if the digital signature is verified against the binary content. Leave unpopulated if a certificate was unchecked.', + example: 'true', + name: 'dll.code_signature.valid', + type: 'boolean', + }, + 'dll.hash.md5': { + category: 'dll', + description: 'MD5 hash.', + name: 'dll.hash.md5', + type: 'keyword', + }, + 'dll.hash.sha1': { + category: 'dll', + description: 'SHA1 hash.', + name: 'dll.hash.sha1', + type: 'keyword', + }, + 'dll.hash.sha256': { + category: 'dll', + description: 'SHA256 hash.', + name: 'dll.hash.sha256', + type: 'keyword', + }, + 'dll.hash.sha512': { + category: 'dll', + description: 'SHA512 hash.', + name: 'dll.hash.sha512', + type: 'keyword', + }, + 'dll.name': { + category: 'dll', + description: 'Name of the library. This generally maps to the name of the file on disk.', + example: 'kernel32.dll', + name: 'dll.name', + type: 'keyword', + }, + 'dll.path': { + category: 'dll', + description: 'Full file path of the library.', + example: 'C:\\Windows\\System32\\kernel32.dll', + name: 'dll.path', + type: 'keyword', + }, + 'dll.pe.company': { + category: 'dll', + description: 'Internal company name of the file, provided at compile-time.', + example: 'Microsoft Corporation', + name: 'dll.pe.company', + type: 'keyword', + }, + 'dll.pe.description': { + category: 'dll', + description: 'Internal description of the file, provided at compile-time.', + example: 'Paint', + name: 'dll.pe.description', + type: 'keyword', + }, + 'dll.pe.file_version': { + category: 'dll', + description: 'Internal version of the file, provided at compile-time.', + example: '6.3.9600.17415', + name: 'dll.pe.file_version', + type: 'keyword', + }, + 'dll.pe.original_file_name': { + category: 'dll', + description: 'Internal name of the file, provided at compile-time.', + example: 'MSPAINT.EXE', + name: 'dll.pe.original_file_name', + type: 'keyword', + }, + 'dll.pe.product': { + category: 'dll', + description: 'Internal product name of the file, provided at compile-time.', + example: 'Microsoft® Windows® Operating System', + name: 'dll.pe.product', + type: 'keyword', + }, + 'dns.answers': { + category: 'dns', + description: + 'An array containing an object for each answer section returned by the server. The main keys that should be present in these objects are defined by ECS. Records that have more information may contain more keys than what ECS defines. Not all DNS data sources give all details about DNS answers. At minimum, answer objects must contain the `data` key. If more information is available, map as much of it to ECS as possible, and add any additional fields to the answer objects as custom fields.', + name: 'dns.answers', + type: 'object', + }, + 'dns.answers.class': { + category: 'dns', + description: 'The class of DNS data contained in this resource record.', + example: 'IN', + name: 'dns.answers.class', + type: 'keyword', + }, + 'dns.answers.data': { + category: 'dns', + description: + 'The data describing the resource. The meaning of this data depends on the type and class of the resource record.', + example: '10.10.10.10', + name: 'dns.answers.data', + type: 'keyword', + }, + 'dns.answers.name': { + category: 'dns', + description: + "The domain name to which this resource record pertains. If a chain of CNAME is being resolved, each answer's `name` should be the one that corresponds with the answer's `data`. It should not simply be the original `question.name` repeated.", + example: 'www.google.com', + name: 'dns.answers.name', + type: 'keyword', + }, + 'dns.answers.ttl': { + category: 'dns', + description: + 'The time interval in seconds that this resource record may be cached before it should be discarded. Zero values mean that the data should not be cached.', + example: 180, + name: 'dns.answers.ttl', + type: 'long', + }, + 'dns.answers.type': { + category: 'dns', + description: 'The type of data contained in this resource record.', + example: 'CNAME', + name: 'dns.answers.type', + type: 'keyword', + }, + 'dns.header_flags': { + category: 'dns', + description: + 'Array of 2 letter DNS header flags. Expected values are: AA, TC, RD, RA, AD, CD, DO.', + example: '["RD","RA"]', + name: 'dns.header_flags', + type: 'keyword', + }, + 'dns.id': { + category: 'dns', + description: + 'The DNS packet identifier assigned by the program that generated the query. The identifier is copied to the response.', + example: 62111, + name: 'dns.id', + type: 'keyword', + }, + 'dns.op_code': { + category: 'dns', + description: + 'The DNS operation code that specifies the kind of query in the message. This value is set by the originator of a query and copied into the response.', + example: 'QUERY', + name: 'dns.op_code', + type: 'keyword', + }, + 'dns.question.class': { + category: 'dns', + description: 'The class of records being queried.', + example: 'IN', + name: 'dns.question.class', + type: 'keyword', + }, + 'dns.question.name': { + category: 'dns', + description: + 'The name being queried. If the name field contains non-printable characters (below 32 or above 126), those characters should be represented as escaped base 10 integers (\\DDD). Back slashes and quotes should be escaped. Tabs, carriage returns, and line feeds should be converted to \\t, \\r, and \\n respectively.', + example: 'www.google.com', + name: 'dns.question.name', + type: 'keyword', + }, + 'dns.question.registered_domain': { + category: 'dns', + description: + 'The highest registered domain, stripped of the subdomain. For example, the registered domain for "foo.google.com" is "google.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', + example: 'google.com', + name: 'dns.question.registered_domain', + type: 'keyword', + }, + 'dns.question.subdomain': { + category: 'dns', + description: + 'The subdomain is all of the labels under the registered_domain. If the domain has multiple levels of subdomain, such as "sub2.sub1.example.com", the subdomain field should contain "sub2.sub1", with no trailing period.', + example: 'www', + name: 'dns.question.subdomain', + type: 'keyword', + }, + 'dns.question.top_level_domain': { + category: 'dns', + description: + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', + example: 'co.uk', + name: 'dns.question.top_level_domain', + type: 'keyword', + }, + 'dns.question.type': { + category: 'dns', + description: 'The type of record being queried.', + example: 'AAAA', + name: 'dns.question.type', + type: 'keyword', + }, + 'dns.resolved_ip': { + category: 'dns', + description: + 'Array containing all IPs seen in `answers.data`. The `answers` array can be difficult to use, because of the variety of data formats it can contain. Extracting all IP addresses seen in there to `dns.resolved_ip` makes it possible to index them as IP addresses, and makes them easier to visualize and query for.', + example: '["10.10.10.10","10.10.10.11"]', + name: 'dns.resolved_ip', + type: 'ip', + }, + 'dns.response_code': { + category: 'dns', + description: 'The DNS response code.', + example: 'NOERROR', + name: 'dns.response_code', + type: 'keyword', + }, + 'dns.type': { + category: 'dns', + description: + 'The type of DNS event captured, query or answer. If your source of DNS events only gives you DNS queries, you should only create dns events of type `dns.type:query`. If your source of DNS events gives you answers as well, you should create one event per query (optionally as soon as the query is seen). And a second event containing all query details as well as an array of answers.', + example: 'answer', + name: 'dns.type', + type: 'keyword', + }, + 'ecs.version': { + category: 'ecs', + description: + 'ECS version this event conforms to. `ecs.version` is a required field and must exist in all events. When querying across multiple indices -- which may conform to slightly different ECS versions -- this field lets integrations adjust to the schema version of the events.', + example: '1.0.0', + name: 'ecs.version', + type: 'keyword', + }, + 'error.code': { + category: 'error', + description: 'Error code describing the error.', + name: 'error.code', + type: 'keyword', + }, + 'error.id': { + category: 'error', + description: 'Unique identifier for the error.', + name: 'error.id', + type: 'keyword', + }, + 'error.message': { + category: 'error', + description: 'Error message.', + name: 'error.message', + type: 'text', + }, + 'error.stack_trace': { + category: 'error', + description: 'The stack trace of this error in plain text.', + name: 'error.stack_trace', + type: 'keyword', + }, + 'error.type': { + category: 'error', + description: 'The type of the error, for example the class name of the exception.', + example: 'java.lang.NullPointerException', + name: 'error.type', + type: 'keyword', + }, + 'event.action': { + category: 'event', + description: + 'The action captured by the event. This describes the information in the event. It is more specific than `event.category`. Examples are `group-add`, `process-started`, `file-created`. The value is normally defined by the implementer.', + example: 'user-password-change', + name: 'event.action', + type: 'keyword', + }, + 'event.category': { + category: 'event', + description: + 'This is one of four ECS Categorization Fields, and indicates the second level in the ECS category hierarchy. `event.category` represents the "big buckets" of ECS categories. For example, filtering on `event.category:process` yields all events relating to process activity. This field is closely related to `event.type`, which is used as a subcategory. This field is an array. This will allow proper categorization of some events that fall in multiple categories.', + example: 'authentication', + name: 'event.category', + type: 'keyword', + }, + 'event.code': { + category: 'event', + description: + 'Identification code for this event, if one exists. Some event sources use event codes to identify messages unambiguously, regardless of message language or wording adjustments over time. An example of this is the Windows Event ID.', + example: 4648, + name: 'event.code', + type: 'keyword', + }, + 'event.created': { + category: 'event', + description: + "event.created contains the date/time when the event was first read by an agent, or by your pipeline. This field is distinct from @timestamp in that @timestamp typically contain the time extracted from the original event. In most situations, these two timestamps will be slightly different. The difference can be used to calculate the delay between your source generating an event, and the time when your agent first processed it. This can be used to monitor your agent's or pipeline's ability to keep up with your event source. In case the two timestamps are identical, @timestamp should be used.", + example: '2016-05-23T08:05:34.857Z', + name: 'event.created', + type: 'date', + }, + 'event.dataset': { + category: 'event', + description: + "Name of the dataset. If an event source publishes more than one type of log or events (e.g. access log, error log), the dataset is used to specify which one the event comes from. It's recommended but not required to start the dataset name with the module name, followed by a dot, then the dataset name.", + example: 'apache.access', + name: 'event.dataset', + type: 'keyword', + }, + 'event.duration': { + category: 'event', + description: + 'Duration of the event in nanoseconds. If event.start and event.end are known this value should be the difference between the end and start time.', + name: 'event.duration', + type: 'long', + format: 'duration', + }, + 'event.end': { + category: 'event', + description: + 'event.end contains the date when the event ended or when the activity was last observed.', + name: 'event.end', + type: 'date', + }, + 'event.hash': { + category: 'event', + description: + 'Hash (perhaps logstash fingerprint) of raw field to be able to demonstrate log integrity.', + example: '123456789012345678901234567890ABCD', + name: 'event.hash', + type: 'keyword', + }, + 'event.id': { + category: 'event', + description: 'Unique ID to describe the event.', + example: '8a4f500d', + name: 'event.id', + type: 'keyword', + }, + 'event.ingested': { + category: 'event', + description: + "Timestamp when an event arrived in the central data store. This is different from `@timestamp`, which is when the event originally occurred. It's also different from `event.created`, which is meant to capture the first time an agent saw the event. In normal conditions, assuming no tampering, the timestamps should chronologically look like this: `@timestamp` < `event.created` < `event.ingested`.", + example: '2016-05-23T08:05:35.101Z', + name: 'event.ingested', + type: 'date', + }, + 'event.kind': { + category: 'event', + description: + 'This is one of four ECS Categorization Fields, and indicates the highest level in the ECS category hierarchy. `event.kind` gives high-level information about what type of information the event contains, without being specific to the contents of the event. For example, values of this field distinguish alert events from metric events. The value of this field can be used to inform how these kinds of events should be handled. They may warrant different retention, different access control, it may also help understand whether the data coming in at a regular interval or not.', + example: 'alert', + name: 'event.kind', + type: 'keyword', + }, + 'event.module': { + category: 'event', + description: + 'Name of the module this data is coming from. If your monitoring agent supports the concept of modules or plugins to process events of a given source (e.g. Apache logs), `event.module` should contain the name of this module.', + example: 'apache', + name: 'event.module', + type: 'keyword', + }, + 'event.original': { + category: 'event', + description: + 'Raw text message of entire event. Used to demonstrate log integrity. This field is not indexed and doc_values are disabled. It cannot be searched, but it can be retrieved from `_source`.', + example: + 'Sep 19 08:26:10 host CEF:0|Security| threatmanager|1.0|100| worm successfully stopped|10|src=10.0.0.1 dst=2.1.2.2spt=1232', + name: 'event.original', + type: 'keyword', + }, + 'event.outcome': { + category: 'event', + description: + 'This is one of four ECS Categorization Fields, and indicates the lowest level in the ECS category hierarchy. `event.outcome` simply denotes whether the event represents a success or a failure from the perspective of the entity that produced the event. Note that when a single transaction is described in multiple events, each event may populate different values of `event.outcome`, according to their perspective. Also note that in the case of a compound event (a single event that contains multiple logical events), this field should be populated with the value that best captures the overall success or failure from the perspective of the event producer. Further note that not all events will have an associated outcome. For example, this field is generally not populated for metric events, events with `event.type:info`, or any events for which an outcome does not make logical sense.', + example: 'success', + name: 'event.outcome', + type: 'keyword', + }, + 'event.provider': { + category: 'event', + description: + 'Source of the event. Event transports such as Syslog or the Windows Event Log typically mention the source of an event. It can be the name of the software that generated the event (e.g. Sysmon, httpd), or of a subsystem of the operating system (kernel, Microsoft-Windows-Security-Auditing).', + example: 'kernel', + name: 'event.provider', + type: 'keyword', + }, + 'event.reference': { + category: 'event', + description: + 'Reference URL linking to additional information about this event. This URL links to a static definition of the this event. Alert events, indicated by `event.kind:alert`, are a common use case for this field.', + example: 'https://system.vendor.com/event/#0001234', + name: 'event.reference', + type: 'keyword', + }, + 'event.risk_score': { + category: 'event', + description: + "Risk score or priority of the event (e.g. security solutions). Use your system's original value here.", + name: 'event.risk_score', + type: 'float', + }, + 'event.risk_score_norm': { + category: 'event', + description: + 'Normalized risk score or priority of the event, on a scale of 0 to 100. This is mainly useful if you use more than one system that assigns risk scores, and you want to see a normalized value across all systems.', + name: 'event.risk_score_norm', + type: 'float', + }, + 'event.sequence': { + category: 'event', + description: + 'Sequence number of the event. The sequence number is a value published by some event sources, to make the exact ordering of events unambiguous, regardless of the timestamp precision.', + name: 'event.sequence', + type: 'long', + format: 'string', + }, + 'event.severity': { + category: 'event', + description: + "The numeric severity of the event according to your event source. What the different severity values mean can be different between sources and use cases. It's up to the implementer to make sure severities are consistent across events from the same source. The Syslog severity belongs in `log.syslog.severity.code`. `event.severity` is meant to represent the severity according to the event source (e.g. firewall, IDS). If the event source does not publish its own severity, you may optionally copy the `log.syslog.severity.code` to `event.severity`.", + example: 7, + name: 'event.severity', + type: 'long', + format: 'string', + }, + 'event.start': { + category: 'event', + description: + 'event.start contains the date when the event started or when the activity was first observed.', + name: 'event.start', + type: 'date', + }, + 'event.timezone': { + category: 'event', + description: + 'This field should be populated when the event\'s timestamp does not include timezone information already (e.g. default Syslog timestamps). It\'s optional otherwise. Acceptable timezone formats are: a canonical ID (e.g. "Europe/Amsterdam"), abbreviated (e.g. "EST") or an HH:mm differential (e.g. "-05:00").', + name: 'event.timezone', + type: 'keyword', + }, + 'event.type': { + category: 'event', + description: + 'This is one of four ECS Categorization Fields, and indicates the third level in the ECS category hierarchy. `event.type` represents a categorization "sub-bucket" that, when used along with the `event.category` field values, enables filtering events down to a level appropriate for single visualization. This field is an array. This will allow proper categorization of some events that fall in multiple event types.', + name: 'event.type', + type: 'keyword', + }, + 'event.url': { + category: 'event', + description: + 'URL linking to an external system to continue investigation of this event. This URL links to another system where in-depth investigation of the specific occurence of this event can take place. Alert events, indicated by `event.kind:alert`, are a common use case for this field.', + example: 'https://mysystem.mydomain.com/alert/5271dedb-f5b0-4218-87f0-4ac4870a38fe', + name: 'event.url', + type: 'keyword', + }, + 'file.accessed': { + category: 'file', + description: + 'Last time the file was accessed. Note that not all filesystems keep track of access time.', + name: 'file.accessed', + type: 'date', + }, + 'file.attributes': { + category: 'file', + description: + "Array of file attributes. Attributes names will vary by platform. Here's a non-exhaustive list of values that are expected in this field: archive, compressed, directory, encrypted, execute, hidden, read, readonly, system, write.", + example: '["readonly", "system"]', + name: 'file.attributes', + type: 'keyword', + }, + 'file.code_signature.exists': { + category: 'file', + description: 'Boolean to capture if a signature is present.', + example: 'true', + name: 'file.code_signature.exists', + type: 'boolean', + }, + 'file.code_signature.status': { + category: 'file', + description: + 'Additional information about the certificate status. This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked.', + example: 'ERROR_UNTRUSTED_ROOT', + name: 'file.code_signature.status', + type: 'keyword', + }, + 'file.code_signature.subject_name': { + category: 'file', + description: 'Subject name of the code signer', + example: 'Microsoft Corporation', + name: 'file.code_signature.subject_name', + type: 'keyword', + }, + 'file.code_signature.trusted': { + category: 'file', + description: + 'Stores the trust status of the certificate chain. Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status.', + example: 'true', + name: 'file.code_signature.trusted', + type: 'boolean', + }, + 'file.code_signature.valid': { + category: 'file', + description: + 'Boolean to capture if the digital signature is verified against the binary content. Leave unpopulated if a certificate was unchecked.', + example: 'true', + name: 'file.code_signature.valid', + type: 'boolean', + }, + 'file.created': { + category: 'file', + description: 'File creation time. Note that not all filesystems store the creation time.', + name: 'file.created', + type: 'date', + }, + 'file.ctime': { + category: 'file', + description: + 'Last time the file attributes or metadata changed. Note that changes to the file content will update `mtime`. This implies `ctime` will be adjusted at the same time, since `mtime` is an attribute of the file.', + name: 'file.ctime', + type: 'date', + }, + 'file.device': { + category: 'file', + description: 'Device that is the source of the file.', + example: 'sda', + name: 'file.device', + type: 'keyword', + }, + 'file.directory': { + category: 'file', + description: + 'Directory where the file is located. It should include the drive letter, when appropriate.', + example: '/home/alice', + name: 'file.directory', + type: 'keyword', + }, + 'file.drive_letter': { + category: 'file', + description: + 'Drive letter where the file is located. This field is only relevant on Windows. The value should be uppercase, and not include the colon.', + example: 'C', + name: 'file.drive_letter', + type: 'keyword', + }, + 'file.extension': { + category: 'file', + description: 'File extension.', + example: 'png', + name: 'file.extension', + type: 'keyword', + }, + 'file.gid': { + category: 'file', + description: 'Primary group ID (GID) of the file.', + example: '1001', + name: 'file.gid', + type: 'keyword', + }, + 'file.group': { + category: 'file', + description: 'Primary group name of the file.', + example: 'alice', + name: 'file.group', + type: 'keyword', + }, + 'file.hash.md5': { + category: 'file', + description: 'MD5 hash.', + name: 'file.hash.md5', + type: 'keyword', + }, + 'file.hash.sha1': { + category: 'file', + description: 'SHA1 hash.', + name: 'file.hash.sha1', + type: 'keyword', + }, + 'file.hash.sha256': { + category: 'file', + description: 'SHA256 hash.', + name: 'file.hash.sha256', + type: 'keyword', + }, + 'file.hash.sha512': { + category: 'file', + description: 'SHA512 hash.', + name: 'file.hash.sha512', + type: 'keyword', + }, + 'file.inode': { + category: 'file', + description: 'Inode representing the file in the filesystem.', + example: '256383', + name: 'file.inode', + type: 'keyword', + }, + 'file.mime_type': { + category: 'file', + description: + 'MIME type should identify the format of the file or stream of bytes using https://www.iana.org/assignments/media-types/media-types.xhtml[IANA official types], where possible. When more than one type is applicable, the most specific type should be used.', + name: 'file.mime_type', + type: 'keyword', + }, + 'file.mode': { + category: 'file', + description: 'Mode of the file in octal representation.', + example: '0640', + name: 'file.mode', + type: 'keyword', + }, + 'file.mtime': { + category: 'file', + description: 'Last time the file content was modified.', + name: 'file.mtime', + type: 'date', + }, + 'file.name': { + category: 'file', + description: 'Name of the file including the extension, without the directory.', + example: 'example.png', + name: 'file.name', + type: 'keyword', + }, + 'file.owner': { + category: 'file', + description: "File owner's username.", + example: 'alice', + name: 'file.owner', + type: 'keyword', + }, + 'file.path': { + category: 'file', + description: + 'Full path to the file, including the file name. It should include the drive letter, when appropriate.', + example: '/home/alice/example.png', + name: 'file.path', + type: 'keyword', + }, + 'file.pe.company': { + category: 'file', + description: 'Internal company name of the file, provided at compile-time.', + example: 'Microsoft Corporation', + name: 'file.pe.company', + type: 'keyword', + }, + 'file.pe.description': { + category: 'file', + description: 'Internal description of the file, provided at compile-time.', + example: 'Paint', + name: 'file.pe.description', + type: 'keyword', + }, + 'file.pe.file_version': { + category: 'file', + description: 'Internal version of the file, provided at compile-time.', + example: '6.3.9600.17415', + name: 'file.pe.file_version', + type: 'keyword', + }, + 'file.pe.original_file_name': { + category: 'file', + description: 'Internal name of the file, provided at compile-time.', + example: 'MSPAINT.EXE', + name: 'file.pe.original_file_name', + type: 'keyword', + }, + 'file.pe.product': { + category: 'file', + description: 'Internal product name of the file, provided at compile-time.', + example: 'Microsoft® Windows® Operating System', + name: 'file.pe.product', + type: 'keyword', + }, + 'file.size': { + category: 'file', + description: 'File size in bytes. Only relevant when `file.type` is "file".', + example: 16384, + name: 'file.size', + type: 'long', + }, + 'file.target_path': { + category: 'file', + description: 'Target path for symlinks.', + name: 'file.target_path', + type: 'keyword', + }, + 'file.type': { + category: 'file', + description: 'File type (file, dir, or symlink).', + example: 'file', + name: 'file.type', + type: 'keyword', + }, + 'file.uid': { + category: 'file', + description: 'The user ID (UID) or security identifier (SID) of the file owner.', + example: '1001', + name: 'file.uid', + type: 'keyword', + }, + 'geo.city_name': { + category: 'geo', + description: 'City name.', + example: 'Montreal', + name: 'geo.city_name', + type: 'keyword', + }, + 'geo.continent_name': { + category: 'geo', + description: 'Name of the continent.', + example: 'North America', + name: 'geo.continent_name', + type: 'keyword', + }, + 'geo.country_iso_code': { + category: 'geo', + description: 'Country ISO code.', + example: 'CA', + name: 'geo.country_iso_code', + type: 'keyword', + }, + 'geo.country_name': { + category: 'geo', + description: 'Country name.', + example: 'Canada', + name: 'geo.country_name', + type: 'keyword', + }, + 'geo.location': { + category: 'geo', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', + name: 'geo.location', + type: 'geo_point', + }, + 'geo.name': { + category: 'geo', + description: + 'User-defined description of a location, at the level of granularity they care about. Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. Not typically used in automated geolocation.', + example: 'boston-dc', + name: 'geo.name', + type: 'keyword', + }, + 'geo.region_iso_code': { + category: 'geo', + description: 'Region ISO code.', + example: 'CA-QC', + name: 'geo.region_iso_code', + type: 'keyword', + }, + 'geo.region_name': { + category: 'geo', + description: 'Region name.', + example: 'Quebec', + name: 'geo.region_name', + type: 'keyword', + }, + 'group.domain': { + category: 'group', + description: + 'Name of the directory the group is a member of. For example, an LDAP or Active Directory domain name.', + name: 'group.domain', + type: 'keyword', + }, + 'group.id': { + category: 'group', + description: 'Unique identifier for the group on the system/platform.', + name: 'group.id', + type: 'keyword', + }, + 'group.name': { + category: 'group', + description: 'Name of the group.', + name: 'group.name', + type: 'keyword', + }, + 'hash.md5': { + category: 'hash', + description: 'MD5 hash.', + name: 'hash.md5', + type: 'keyword', + }, + 'hash.sha1': { + category: 'hash', + description: 'SHA1 hash.', + name: 'hash.sha1', + type: 'keyword', + }, + 'hash.sha256': { + category: 'hash', + description: 'SHA256 hash.', + name: 'hash.sha256', + type: 'keyword', + }, + 'hash.sha512': { + category: 'hash', + description: 'SHA512 hash.', + name: 'hash.sha512', + type: 'keyword', + }, + 'host.architecture': { + category: 'host', + description: 'Operating system architecture.', + example: 'x86_64', + name: 'host.architecture', + type: 'keyword', + }, + 'host.domain': { + category: 'host', + description: + "Name of the domain of which the host is a member. For example, on Windows this could be the host's Active Directory domain or NetBIOS domain name. For Linux this could be the domain of the host's LDAP provider.", + example: 'CONTOSO', + name: 'host.domain', + type: 'keyword', + }, + 'host.geo.city_name': { + category: 'host', + description: 'City name.', + example: 'Montreal', + name: 'host.geo.city_name', + type: 'keyword', + }, + 'host.geo.continent_name': { + category: 'host', + description: 'Name of the continent.', + example: 'North America', + name: 'host.geo.continent_name', + type: 'keyword', + }, + 'host.geo.country_iso_code': { + category: 'host', + description: 'Country ISO code.', + example: 'CA', + name: 'host.geo.country_iso_code', + type: 'keyword', + }, + 'host.geo.country_name': { + category: 'host', + description: 'Country name.', + example: 'Canada', + name: 'host.geo.country_name', + type: 'keyword', + }, + 'host.geo.location': { + category: 'host', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', + name: 'host.geo.location', + type: 'geo_point', + }, + 'host.geo.name': { + category: 'host', + description: + 'User-defined description of a location, at the level of granularity they care about. Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. Not typically used in automated geolocation.', + example: 'boston-dc', + name: 'host.geo.name', + type: 'keyword', + }, + 'host.geo.region_iso_code': { + category: 'host', + description: 'Region ISO code.', + example: 'CA-QC', + name: 'host.geo.region_iso_code', + type: 'keyword', + }, + 'host.geo.region_name': { + category: 'host', + description: 'Region name.', + example: 'Quebec', + name: 'host.geo.region_name', + type: 'keyword', + }, + 'host.hostname': { + category: 'host', + description: + 'Hostname of the host. It normally contains what the `hostname` command returns on the host machine.', + name: 'host.hostname', + type: 'keyword', + }, + 'host.id': { + category: 'host', + description: + 'Unique host id. As hostname is not always unique, use values that are meaningful in your environment. Example: The current usage of `beat.name`.', + name: 'host.id', + type: 'keyword', + }, + 'host.ip': { + category: 'host', + description: 'Host ip addresses.', + name: 'host.ip', + type: 'ip', + }, + 'host.mac': { + category: 'host', + description: 'Host mac addresses.', + name: 'host.mac', + type: 'keyword', + }, + 'host.name': { + category: 'host', + description: + 'Name of the host. It can contain what `hostname` returns on Unix systems, the fully qualified domain name, or a name specified by the user. The sender decides which value to use.', + name: 'host.name', + type: 'keyword', + }, + 'host.os.family': { + category: 'host', + description: 'OS family (such as redhat, debian, freebsd, windows).', + example: 'debian', + name: 'host.os.family', + type: 'keyword', + }, + 'host.os.full': { + category: 'host', + description: 'Operating system name, including the version or code name.', + example: 'Mac OS Mojave', + name: 'host.os.full', + type: 'keyword', + }, + 'host.os.kernel': { + category: 'host', + description: 'Operating system kernel version as a raw string.', + example: '4.4.0-112-generic', + name: 'host.os.kernel', + type: 'keyword', + }, + 'host.os.name': { + category: 'host', + description: 'Operating system name, without the version.', + example: 'Mac OS X', + name: 'host.os.name', + type: 'keyword', + }, + 'host.os.platform': { + category: 'host', + description: 'Operating system platform (such centos, ubuntu, windows).', + example: 'darwin', + name: 'host.os.platform', + type: 'keyword', + }, + 'host.os.version': { + category: 'host', + description: 'Operating system version as a raw string.', + example: '10.14.1', + name: 'host.os.version', + type: 'keyword', + }, + 'host.type': { + category: 'host', + description: + 'Type of host. For Cloud providers this can be the machine type like `t2.medium`. If vm, this could be the container, for example, or other information meaningful in your environment.', + name: 'host.type', + type: 'keyword', + }, + 'host.uptime': { + category: 'host', + description: 'Seconds the host has been up.', + example: 1325, + name: 'host.uptime', + type: 'long', + }, + 'host.user.domain': { + category: 'host', + description: + 'Name of the directory the user is a member of. For example, an LDAP or Active Directory domain name.', + name: 'host.user.domain', + type: 'keyword', + }, + 'host.user.email': { + category: 'host', + description: 'User email address.', + name: 'host.user.email', + type: 'keyword', + }, + 'host.user.full_name': { + category: 'host', + description: "User's full name, if available.", + example: 'Albert Einstein', + name: 'host.user.full_name', + type: 'keyword', + }, + 'host.user.group.domain': { + category: 'host', + description: + 'Name of the directory the group is a member of. For example, an LDAP or Active Directory domain name.', + name: 'host.user.group.domain', + type: 'keyword', + }, + 'host.user.group.id': { + category: 'host', + description: 'Unique identifier for the group on the system/platform.', + name: 'host.user.group.id', + type: 'keyword', + }, + 'host.user.group.name': { + category: 'host', + description: 'Name of the group.', + name: 'host.user.group.name', + type: 'keyword', + }, + 'host.user.hash': { + category: 'host', + description: + 'Unique user hash to correlate information for a user in anonymized form. Useful if `user.id` or `user.name` contain confidential information and cannot be used.', + name: 'host.user.hash', + type: 'keyword', + }, + 'host.user.id': { + category: 'host', + description: 'Unique identifiers of the user.', + name: 'host.user.id', + type: 'keyword', + }, + 'host.user.name': { + category: 'host', + description: 'Short name or login of the user.', + example: 'albert', + name: 'host.user.name', + type: 'keyword', + }, + 'http.request.body.bytes': { + category: 'http', + description: 'Size in bytes of the request body.', + example: 887, + name: 'http.request.body.bytes', + type: 'long', + format: 'bytes', + }, + 'http.request.body.content': { + category: 'http', + description: 'The full HTTP request body.', + example: 'Hello world', + name: 'http.request.body.content', + type: 'keyword', + }, + 'http.request.bytes': { + category: 'http', + description: 'Total size in bytes of the request (body and headers).', + example: 1437, + name: 'http.request.bytes', + type: 'long', + format: 'bytes', + }, + 'http.request.method': { + category: 'http', + description: + 'HTTP request method. The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS".', + example: 'get, post, put', + name: 'http.request.method', + type: 'keyword', + }, + 'http.request.referrer': { + category: 'http', + description: 'Referrer for this HTTP request.', + example: 'https://blog.example.com/', + name: 'http.request.referrer', + type: 'keyword', + }, + 'http.response.body.bytes': { + category: 'http', + description: 'Size in bytes of the response body.', + example: 887, + name: 'http.response.body.bytes', + type: 'long', + format: 'bytes', + }, + 'http.response.body.content': { + category: 'http', + description: 'The full HTTP response body.', + example: 'Hello world', + name: 'http.response.body.content', + type: 'keyword', + }, + 'http.response.bytes': { + category: 'http', + description: 'Total size in bytes of the response (body and headers).', + example: 1437, + name: 'http.response.bytes', + type: 'long', + format: 'bytes', + }, + 'http.response.status_code': { + category: 'http', + description: 'HTTP response status code.', + example: 404, + name: 'http.response.status_code', + type: 'long', + format: 'string', + }, + 'http.version': { + category: 'http', + description: 'HTTP version.', + example: 1.1, + name: 'http.version', + type: 'keyword', + }, + 'interface.alias': { + category: 'interface', + description: + 'Interface alias as reported by the system, typically used in firewall implementations for e.g. inside, outside, or dmz logical interface naming.', + example: 'outside', + name: 'interface.alias', + type: 'keyword', + }, + 'interface.id': { + category: 'interface', + description: 'Interface ID as reported by an observer (typically SNMP interface ID).', + example: 10, + name: 'interface.id', + type: 'keyword', + }, + 'interface.name': { + category: 'interface', + description: 'Interface name as reported by the system.', + example: 'eth0', + name: 'interface.name', + type: 'keyword', + }, + 'log.level': { + category: 'log', + description: + "Original log level of the log event. If the source of the event provides a log level or textual severity, this is the one that goes in `log.level`. If your source doesn't specify one, you may put your event transport's severity here (e.g. Syslog severity). Some examples are `warn`, `err`, `i`, `informational`.", + example: 'error', + name: 'log.level', + type: 'keyword', + }, + 'log.logger': { + category: 'log', + description: + 'The name of the logger inside an application. This is usually the name of the class which initialized the logger, or can be a custom name.', + example: 'org.elasticsearch.bootstrap.Bootstrap', + name: 'log.logger', + type: 'keyword', + }, + 'log.origin.file.line': { + category: 'log', + description: + 'The line number of the file containing the source code which originated the log event.', + example: 42, + name: 'log.origin.file.line', + type: 'integer', + }, + 'log.origin.file.name': { + category: 'log', + description: + 'The name of the file containing the source code which originated the log event. Note that this is not the name of the log file.', + example: 'Bootstrap.java', + name: 'log.origin.file.name', + type: 'keyword', + }, + 'log.origin.function': { + category: 'log', + description: 'The name of the function or method which originated the log event.', + example: 'init', + name: 'log.origin.function', + type: 'keyword', + }, + 'log.original': { + category: 'log', + description: + "This is the original log message and contains the full log message before splitting it up in multiple parts. In contrast to the `message` field which can contain an extracted part of the log message, this field contains the original, full log message. It can have already some modifications applied like encoding or new lines removed to clean up the log message. This field is not indexed and doc_values are disabled so it can't be queried but the value can be retrieved from `_source`.", + example: 'Sep 19 08:26:10 localhost My log', + name: 'log.original', + type: 'keyword', + }, + 'log.syslog': { + category: 'log', + description: + 'The Syslog metadata of the event, if the event was transmitted via Syslog. Please see RFCs 5424 or 3164.', + name: 'log.syslog', + type: 'object', + }, + 'log.syslog.facility.code': { + category: 'log', + description: + 'The Syslog numeric facility of the log event, if available. According to RFCs 5424 and 3164, this value should be an integer between 0 and 23.', + example: 23, + name: 'log.syslog.facility.code', + type: 'long', + format: 'string', + }, + 'log.syslog.facility.name': { + category: 'log', + description: 'The Syslog text-based facility of the log event, if available.', + example: 'local7', + name: 'log.syslog.facility.name', + type: 'keyword', + }, + 'log.syslog.priority': { + category: 'log', + description: + 'Syslog numeric priority of the event, if available. According to RFCs 5424 and 3164, the priority is 8 * facility + severity. This number is therefore expected to contain a value between 0 and 191.', + example: 135, + name: 'log.syslog.priority', + type: 'long', + format: 'string', + }, + 'log.syslog.severity.code': { + category: 'log', + description: + "The Syslog numeric severity of the log event, if available. If the event source publishing via Syslog provides a different numeric severity value (e.g. firewall, IDS), your source's numeric severity should go to `event.severity`. If the event source does not specify a distinct severity, you can optionally copy the Syslog severity to `event.severity`.", + example: 3, + name: 'log.syslog.severity.code', + type: 'long', + }, + 'log.syslog.severity.name': { + category: 'log', + description: + "The Syslog numeric severity of the log event, if available. If the event source publishing via Syslog provides a different severity value (e.g. firewall, IDS), your source's text severity should go to `log.level`. If the event source does not specify a distinct severity, you can optionally copy the Syslog severity to `log.level`.", + example: 'Error', + name: 'log.syslog.severity.name', + type: 'keyword', + }, + 'network.application': { + category: 'network', + description: + 'A name given to an application level protocol. This can be arbitrarily assigned for things like microservices, but also apply to things like skype, icq, facebook, twitter. This would be used in situations where the vendor or service can be decoded such as from the source/dest IP owners, ports, or wire format. The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS".', + example: 'aim', + name: 'network.application', + type: 'keyword', + }, + 'network.bytes': { + category: 'network', + description: + 'Total bytes transferred in both directions. If `source.bytes` and `destination.bytes` are known, `network.bytes` is their sum.', + example: 368, + name: 'network.bytes', + type: 'long', + format: 'bytes', + }, + 'network.community_id': { + category: 'network', + description: + 'A hash of source and destination IPs and ports, as well as the protocol used in a communication. This is a tool-agnostic standard to identify flows. Learn more at https://github.com/corelight/community-id-spec.', + example: '1:hO+sN4H+MG5MY/8hIrXPqc4ZQz0=', + name: 'network.community_id', + type: 'keyword', + }, + 'network.direction': { + category: 'network', + description: + "Direction of the network traffic. Recommended values are: * inbound * outbound * internal * external * unknown When mapping events from a host-based monitoring context, populate this field from the host's point of view. When mapping events from a network or perimeter-based monitoring context, populate this field from the point of view of your network perimeter.", + example: 'inbound', + name: 'network.direction', + type: 'keyword', + }, + 'network.forwarded_ip': { + category: 'network', + description: 'Host IP address when the source IP address is the proxy.', + example: '192.1.1.2', + name: 'network.forwarded_ip', + type: 'ip', + }, + 'network.iana_number': { + category: 'network', + description: + 'IANA Protocol Number (https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml). Standardized list of protocols. This aligns well with NetFlow and sFlow related logs which use the IANA Protocol Number.', + example: 6, + name: 'network.iana_number', + type: 'keyword', + }, + 'network.inner': { + category: 'network', + description: + 'Network.inner fields are added in addition to network.vlan fields to describe the innermost VLAN when q-in-q VLAN tagging is present. Allowed fields include vlan.id and vlan.name. Inner vlan fields are typically used when sending traffic with multiple 802.1q encapsulations to a network sensor (e.g. Zeek, Wireshark.)', + name: 'network.inner', + type: 'object', + }, + 'network.inner.vlan.id': { + category: 'network', + description: 'VLAN ID as reported by the observer.', + example: 10, + name: 'network.inner.vlan.id', + type: 'keyword', + }, + 'network.inner.vlan.name': { + category: 'network', + description: 'Optional VLAN name as reported by the observer.', + example: 'outside', + name: 'network.inner.vlan.name', + type: 'keyword', + }, + 'network.name': { + category: 'network', + description: 'Name given by operators to sections of their network.', + example: 'Guest Wifi', + name: 'network.name', + type: 'keyword', + }, + 'network.packets': { + category: 'network', + description: + 'Total packets transferred in both directions. If `source.packets` and `destination.packets` are known, `network.packets` is their sum.', + example: 24, + name: 'network.packets', + type: 'long', + }, + 'network.protocol': { + category: 'network', + description: + 'L7 Network protocol name. ex. http, lumberjack, transport protocol. The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS".', + example: 'http', + name: 'network.protocol', + type: 'keyword', + }, + 'network.transport': { + category: 'network', + description: + 'Same as network.iana_number, but instead using the Keyword name of the transport layer (udp, tcp, ipv6-icmp, etc.) The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS".', + example: 'tcp', + name: 'network.transport', + type: 'keyword', + }, + 'network.type': { + category: 'network', + description: + 'In the OSI Model this would be the Network Layer. ipv4, ipv6, ipsec, pim, etc The field value must be normalized to lowercase for querying. See the documentation section "Implementing ECS".', + example: 'ipv4', + name: 'network.type', + type: 'keyword', + }, + 'network.vlan.id': { + category: 'network', + description: 'VLAN ID as reported by the observer.', + example: 10, + name: 'network.vlan.id', + type: 'keyword', + }, + 'network.vlan.name': { + category: 'network', + description: 'Optional VLAN name as reported by the observer.', + example: 'outside', + name: 'network.vlan.name', + type: 'keyword', + }, + 'observer.egress': { + category: 'observer', + description: + 'Observer.egress holds information like interface number and name, vlan, and zone information to classify egress traffic. Single armed monitoring such as a network sensor on a span port should only use observer.ingress to categorize traffic.', + name: 'observer.egress', + type: 'object', + }, + 'observer.egress.interface.alias': { + category: 'observer', + description: + 'Interface alias as reported by the system, typically used in firewall implementations for e.g. inside, outside, or dmz logical interface naming.', + example: 'outside', + name: 'observer.egress.interface.alias', + type: 'keyword', + }, + 'observer.egress.interface.id': { + category: 'observer', + description: 'Interface ID as reported by an observer (typically SNMP interface ID).', + example: 10, + name: 'observer.egress.interface.id', + type: 'keyword', + }, + 'observer.egress.interface.name': { + category: 'observer', + description: 'Interface name as reported by the system.', + example: 'eth0', + name: 'observer.egress.interface.name', + type: 'keyword', + }, + 'observer.egress.vlan.id': { + category: 'observer', + description: 'VLAN ID as reported by the observer.', + example: 10, + name: 'observer.egress.vlan.id', + type: 'keyword', + }, + 'observer.egress.vlan.name': { + category: 'observer', + description: 'Optional VLAN name as reported by the observer.', + example: 'outside', + name: 'observer.egress.vlan.name', + type: 'keyword', + }, + 'observer.egress.zone': { + category: 'observer', + description: + 'Network zone of outbound traffic as reported by the observer to categorize the destination area of egress traffic, e.g. Internal, External, DMZ, HR, Legal, etc.', + example: 'Public_Internet', + name: 'observer.egress.zone', + type: 'keyword', + }, + 'observer.geo.city_name': { + category: 'observer', + description: 'City name.', + example: 'Montreal', + name: 'observer.geo.city_name', + type: 'keyword', + }, + 'observer.geo.continent_name': { + category: 'observer', + description: 'Name of the continent.', + example: 'North America', + name: 'observer.geo.continent_name', + type: 'keyword', + }, + 'observer.geo.country_iso_code': { + category: 'observer', + description: 'Country ISO code.', + example: 'CA', + name: 'observer.geo.country_iso_code', + type: 'keyword', + }, + 'observer.geo.country_name': { + category: 'observer', + description: 'Country name.', + example: 'Canada', + name: 'observer.geo.country_name', + type: 'keyword', + }, + 'observer.geo.location': { + category: 'observer', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', + name: 'observer.geo.location', + type: 'geo_point', + }, + 'observer.geo.name': { + category: 'observer', + description: + 'User-defined description of a location, at the level of granularity they care about. Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. Not typically used in automated geolocation.', + example: 'boston-dc', + name: 'observer.geo.name', + type: 'keyword', + }, + 'observer.geo.region_iso_code': { + category: 'observer', + description: 'Region ISO code.', + example: 'CA-QC', + name: 'observer.geo.region_iso_code', + type: 'keyword', + }, + 'observer.geo.region_name': { + category: 'observer', + description: 'Region name.', + example: 'Quebec', + name: 'observer.geo.region_name', + type: 'keyword', + }, + 'observer.hostname': { + category: 'observer', + description: 'Hostname of the observer.', + name: 'observer.hostname', + type: 'keyword', + }, + 'observer.ingress': { + category: 'observer', + description: + 'Observer.ingress holds information like interface number and name, vlan, and zone information to classify ingress traffic. Single armed monitoring such as a network sensor on a span port should only use observer.ingress to categorize traffic.', + name: 'observer.ingress', + type: 'object', + }, + 'observer.ingress.interface.alias': { + category: 'observer', + description: + 'Interface alias as reported by the system, typically used in firewall implementations for e.g. inside, outside, or dmz logical interface naming.', + example: 'outside', + name: 'observer.ingress.interface.alias', + type: 'keyword', + }, + 'observer.ingress.interface.id': { + category: 'observer', + description: 'Interface ID as reported by an observer (typically SNMP interface ID).', + example: 10, + name: 'observer.ingress.interface.id', + type: 'keyword', + }, + 'observer.ingress.interface.name': { + category: 'observer', + description: 'Interface name as reported by the system.', + example: 'eth0', + name: 'observer.ingress.interface.name', + type: 'keyword', + }, + 'observer.ingress.vlan.id': { + category: 'observer', + description: 'VLAN ID as reported by the observer.', + example: 10, + name: 'observer.ingress.vlan.id', + type: 'keyword', + }, + 'observer.ingress.vlan.name': { + category: 'observer', + description: 'Optional VLAN name as reported by the observer.', + example: 'outside', + name: 'observer.ingress.vlan.name', + type: 'keyword', + }, + 'observer.ingress.zone': { + category: 'observer', + description: + 'Network zone of incoming traffic as reported by the observer to categorize the source area of ingress traffic. e.g. internal, External, DMZ, HR, Legal, etc.', + example: 'DMZ', + name: 'observer.ingress.zone', + type: 'keyword', + }, + 'observer.ip': { + category: 'observer', + description: 'IP addresses of the observer.', + name: 'observer.ip', + type: 'ip', + }, + 'observer.mac': { + category: 'observer', + description: 'MAC addresses of the observer', + name: 'observer.mac', + type: 'keyword', + }, + 'observer.name': { + category: 'observer', + description: + 'Custom name of the observer. This is a name that can be given to an observer. This can be helpful for example if multiple firewalls of the same model are used in an organization. If no custom name is needed, the field can be left empty.', + example: '1_proxySG', + name: 'observer.name', + type: 'keyword', + }, + 'observer.os.family': { + category: 'observer', + description: 'OS family (such as redhat, debian, freebsd, windows).', + example: 'debian', + name: 'observer.os.family', + type: 'keyword', + }, + 'observer.os.full': { + category: 'observer', + description: 'Operating system name, including the version or code name.', + example: 'Mac OS Mojave', + name: 'observer.os.full', + type: 'keyword', + }, + 'observer.os.kernel': { + category: 'observer', + description: 'Operating system kernel version as a raw string.', + example: '4.4.0-112-generic', + name: 'observer.os.kernel', + type: 'keyword', + }, + 'observer.os.name': { + category: 'observer', + description: 'Operating system name, without the version.', + example: 'Mac OS X', + name: 'observer.os.name', + type: 'keyword', + }, + 'observer.os.platform': { + category: 'observer', + description: 'Operating system platform (such centos, ubuntu, windows).', + example: 'darwin', + name: 'observer.os.platform', + type: 'keyword', + }, + 'observer.os.version': { + category: 'observer', + description: 'Operating system version as a raw string.', + example: '10.14.1', + name: 'observer.os.version', + type: 'keyword', + }, + 'observer.product': { + category: 'observer', + description: 'The product name of the observer.', + example: 's200', + name: 'observer.product', + type: 'keyword', + }, + 'observer.serial_number': { + category: 'observer', + description: 'Observer serial number.', + name: 'observer.serial_number', + type: 'keyword', + }, + 'observer.type': { + category: 'observer', + description: + 'The type of the observer the data is coming from. There is no predefined list of observer types. Some examples are `forwarder`, `firewall`, `ids`, `ips`, `proxy`, `poller`, `sensor`, `APM server`.', + example: 'firewall', + name: 'observer.type', + type: 'keyword', + }, + 'observer.vendor': { + category: 'observer', + description: 'Vendor name of the observer.', + example: 'Symantec', + name: 'observer.vendor', + type: 'keyword', + }, + 'observer.version': { + category: 'observer', + description: 'Observer version.', + name: 'observer.version', + type: 'keyword', + }, + 'organization.id': { + category: 'organization', + description: 'Unique identifier for the organization.', + name: 'organization.id', + type: 'keyword', + }, + 'organization.name': { + category: 'organization', + description: 'Organization name.', + name: 'organization.name', + type: 'keyword', + }, + 'os.family': { + category: 'os', + description: 'OS family (such as redhat, debian, freebsd, windows).', + example: 'debian', + name: 'os.family', + type: 'keyword', + }, + 'os.full': { + category: 'os', + description: 'Operating system name, including the version or code name.', + example: 'Mac OS Mojave', + name: 'os.full', + type: 'keyword', + }, + 'os.kernel': { + category: 'os', + description: 'Operating system kernel version as a raw string.', + example: '4.4.0-112-generic', + name: 'os.kernel', + type: 'keyword', + }, + 'os.name': { + category: 'os', + description: 'Operating system name, without the version.', + example: 'Mac OS X', + name: 'os.name', + type: 'keyword', + }, + 'os.platform': { + category: 'os', + description: 'Operating system platform (such centos, ubuntu, windows).', + example: 'darwin', + name: 'os.platform', + type: 'keyword', + }, + 'os.version': { + category: 'os', + description: 'Operating system version as a raw string.', + example: '10.14.1', + name: 'os.version', + type: 'keyword', + }, + 'package.architecture': { + category: 'package', + description: 'Package architecture.', + example: 'x86_64', + name: 'package.architecture', + type: 'keyword', + }, + 'package.build_version': { + category: 'package', + description: + 'Additional information about the build version of the installed package. For example use the commit SHA of a non-released package.', + example: '36f4f7e89dd61b0988b12ee000b98966867710cd', + name: 'package.build_version', + type: 'keyword', + }, + 'package.checksum': { + category: 'package', + description: 'Checksum of the installed package for verification.', + example: '68b329da9893e34099c7d8ad5cb9c940', + name: 'package.checksum', + type: 'keyword', + }, + 'package.description': { + category: 'package', + description: 'Description of the package.', + example: 'Open source programming language to build simple/reliable/efficient software.', + name: 'package.description', + type: 'keyword', + }, + 'package.install_scope': { + category: 'package', + description: 'Indicating how the package was installed, e.g. user-local, global.', + example: 'global', + name: 'package.install_scope', + type: 'keyword', + }, + 'package.installed': { + category: 'package', + description: 'Time when package was installed.', + name: 'package.installed', + type: 'date', + }, + 'package.license': { + category: 'package', + description: + 'License under which the package was released. Use a short name, e.g. the license identifier from SPDX License List where possible (https://spdx.org/licenses/).', + example: 'Apache License 2.0', + name: 'package.license', + type: 'keyword', + }, + 'package.name': { + category: 'package', + description: 'Package name', + example: 'go', + name: 'package.name', + type: 'keyword', + }, + 'package.path': { + category: 'package', + description: 'Path where the package is installed.', + example: '/usr/local/Cellar/go/1.12.9/', + name: 'package.path', + type: 'keyword', + }, + 'package.reference': { + category: 'package', + description: 'Home page or reference URL of the software in this package, if available.', + example: 'https://golang.org', + name: 'package.reference', + type: 'keyword', + }, + 'package.size': { + category: 'package', + description: 'Package size in bytes.', + example: 62231, + name: 'package.size', + type: 'long', + format: 'string', + }, + 'package.type': { + category: 'package', + description: + 'Type of package. This should contain the package file type, rather than the package manager name. Examples: rpm, dpkg, brew, npm, gem, nupkg, jar.', + example: 'rpm', + name: 'package.type', + type: 'keyword', + }, + 'package.version': { + category: 'package', + description: 'Package version', + example: '1.12.9', + name: 'package.version', + type: 'keyword', + }, + 'pe.company': { + category: 'pe', + description: 'Internal company name of the file, provided at compile-time.', + example: 'Microsoft Corporation', + name: 'pe.company', + type: 'keyword', + }, + 'pe.description': { + category: 'pe', + description: 'Internal description of the file, provided at compile-time.', + example: 'Paint', + name: 'pe.description', + type: 'keyword', + }, + 'pe.file_version': { + category: 'pe', + description: 'Internal version of the file, provided at compile-time.', + example: '6.3.9600.17415', + name: 'pe.file_version', + type: 'keyword', + }, + 'pe.original_file_name': { + category: 'pe', + description: 'Internal name of the file, provided at compile-time.', + example: 'MSPAINT.EXE', + name: 'pe.original_file_name', + type: 'keyword', + }, + 'pe.product': { + category: 'pe', + description: 'Internal product name of the file, provided at compile-time.', + example: 'Microsoft® Windows® Operating System', + name: 'pe.product', + type: 'keyword', + }, + 'process.args': { + category: 'process', + description: + 'Array of process arguments, starting with the absolute path to the executable. May be filtered to protect sensitive information.', + example: '["/usr/bin/ssh","-l","user","10.0.0.16"]', + name: 'process.args', + type: 'keyword', + }, + 'process.args_count': { + category: 'process', + description: + 'Length of the process.args array. This field can be useful for querying or performing bucket analysis on how many arguments were provided to start a process. More arguments may be an indication of suspicious activity.', + example: 4, + name: 'process.args_count', + type: 'long', + }, + 'process.code_signature.exists': { + category: 'process', + description: 'Boolean to capture if a signature is present.', + example: 'true', + name: 'process.code_signature.exists', + type: 'boolean', + }, + 'process.code_signature.status': { + category: 'process', + description: + 'Additional information about the certificate status. This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked.', + example: 'ERROR_UNTRUSTED_ROOT', + name: 'process.code_signature.status', + type: 'keyword', + }, + 'process.code_signature.subject_name': { + category: 'process', + description: 'Subject name of the code signer', + example: 'Microsoft Corporation', + name: 'process.code_signature.subject_name', + type: 'keyword', + }, + 'process.code_signature.trusted': { + category: 'process', + description: + 'Stores the trust status of the certificate chain. Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status.', + example: 'true', + name: 'process.code_signature.trusted', + type: 'boolean', + }, + 'process.code_signature.valid': { + category: 'process', + description: + 'Boolean to capture if the digital signature is verified against the binary content. Leave unpopulated if a certificate was unchecked.', + example: 'true', + name: 'process.code_signature.valid', + type: 'boolean', + }, + 'process.command_line': { + category: 'process', + description: + 'Full command line that started the process, including the absolute path to the executable, and all arguments. Some arguments may be filtered to protect sensitive information.', + example: '/usr/bin/ssh -l user 10.0.0.16', + name: 'process.command_line', + type: 'keyword', + }, + 'process.entity_id': { + category: 'process', + description: + 'Unique identifier for the process. The implementation of this is specified by the data source, but some examples of what could be used here are a process-generated UUID, Sysmon Process GUIDs, or a hash of some uniquely identifying components of a process. Constructing a globally unique identifier is a common practice to mitigate PID reuse as well as to identify a specific process over time, across multiple monitored hosts.', + example: 'c2c455d9f99375d', + name: 'process.entity_id', + type: 'keyword', + }, + 'process.executable': { + category: 'process', + description: 'Absolute path to the process executable.', + example: '/usr/bin/ssh', + name: 'process.executable', + type: 'keyword', + }, + 'process.exit_code': { + category: 'process', + description: + 'The exit code of the process, if this is a termination event. The field should be absent if there is no exit code for the event (e.g. process start).', + example: 137, + name: 'process.exit_code', + type: 'long', + }, + 'process.hash.md5': { + category: 'process', + description: 'MD5 hash.', + name: 'process.hash.md5', + type: 'keyword', + }, + 'process.hash.sha1': { + category: 'process', + description: 'SHA1 hash.', + name: 'process.hash.sha1', + type: 'keyword', + }, + 'process.hash.sha256': { + category: 'process', + description: 'SHA256 hash.', + name: 'process.hash.sha256', + type: 'keyword', + }, + 'process.hash.sha512': { + category: 'process', + description: 'SHA512 hash.', + name: 'process.hash.sha512', + type: 'keyword', + }, + 'process.name': { + category: 'process', + description: 'Process name. Sometimes called program name or similar.', + example: 'ssh', + name: 'process.name', + type: 'keyword', + }, + 'process.parent.args': { + category: 'process', + description: 'Array of process arguments. May be filtered to protect sensitive information.', + example: '["ssh","-l","user","10.0.0.16"]', + name: 'process.parent.args', + type: 'keyword', + }, + 'process.parent.args_count': { + category: 'process', + description: + 'Length of the process.args array. This field can be useful for querying or performing bucket analysis on how many arguments were provided to start a process. More arguments may be an indication of suspicious activity.', + example: 4, + name: 'process.parent.args_count', + type: 'long', + }, + 'process.parent.code_signature.exists': { + category: 'process', + description: 'Boolean to capture if a signature is present.', + example: 'true', + name: 'process.parent.code_signature.exists', + type: 'boolean', + }, + 'process.parent.code_signature.status': { + category: 'process', + description: + 'Additional information about the certificate status. This is useful for logging cryptographic errors with the certificate validity or trust status. Leave unpopulated if the validity or trust of the certificate was unchecked.', + example: 'ERROR_UNTRUSTED_ROOT', + name: 'process.parent.code_signature.status', + type: 'keyword', + }, + 'process.parent.code_signature.subject_name': { + category: 'process', + description: 'Subject name of the code signer', + example: 'Microsoft Corporation', + name: 'process.parent.code_signature.subject_name', + type: 'keyword', + }, + 'process.parent.code_signature.trusted': { + category: 'process', + description: + 'Stores the trust status of the certificate chain. Validating the trust of the certificate chain may be complicated, and this field should only be populated by tools that actively check the status.', + example: 'true', + name: 'process.parent.code_signature.trusted', + type: 'boolean', + }, + 'process.parent.code_signature.valid': { + category: 'process', + description: + 'Boolean to capture if the digital signature is verified against the binary content. Leave unpopulated if a certificate was unchecked.', + example: 'true', + name: 'process.parent.code_signature.valid', + type: 'boolean', + }, + 'process.parent.command_line': { + category: 'process', + description: + 'Full command line that started the process, including the absolute path to the executable, and all arguments. Some arguments may be filtered to protect sensitive information.', + example: '/usr/bin/ssh -l user 10.0.0.16', + name: 'process.parent.command_line', + type: 'keyword', + }, + 'process.parent.entity_id': { + category: 'process', + description: + 'Unique identifier for the process. The implementation of this is specified by the data source, but some examples of what could be used here are a process-generated UUID, Sysmon Process GUIDs, or a hash of some uniquely identifying components of a process. Constructing a globally unique identifier is a common practice to mitigate PID reuse as well as to identify a specific process over time, across multiple monitored hosts.', + example: 'c2c455d9f99375d', + name: 'process.parent.entity_id', + type: 'keyword', + }, + 'process.parent.executable': { + category: 'process', + description: 'Absolute path to the process executable.', + example: '/usr/bin/ssh', + name: 'process.parent.executable', + type: 'keyword', + }, + 'process.parent.exit_code': { + category: 'process', + description: + 'The exit code of the process, if this is a termination event. The field should be absent if there is no exit code for the event (e.g. process start).', + example: 137, + name: 'process.parent.exit_code', + type: 'long', + }, + 'process.parent.hash.md5': { + category: 'process', + description: 'MD5 hash.', + name: 'process.parent.hash.md5', + type: 'keyword', + }, + 'process.parent.hash.sha1': { + category: 'process', + description: 'SHA1 hash.', + name: 'process.parent.hash.sha1', + type: 'keyword', + }, + 'process.parent.hash.sha256': { + category: 'process', + description: 'SHA256 hash.', + name: 'process.parent.hash.sha256', + type: 'keyword', + }, + 'process.parent.hash.sha512': { + category: 'process', + description: 'SHA512 hash.', + name: 'process.parent.hash.sha512', + type: 'keyword', + }, + 'process.parent.name': { + category: 'process', + description: 'Process name. Sometimes called program name or similar.', + example: 'ssh', + name: 'process.parent.name', + type: 'keyword', + }, + 'process.parent.pgid': { + category: 'process', + description: 'Identifier of the group of processes the process belongs to.', + name: 'process.parent.pgid', + type: 'long', + format: 'string', + }, + 'process.parent.pid': { + category: 'process', + description: 'Process id.', + example: 4242, + name: 'process.parent.pid', + type: 'long', + format: 'string', + }, + 'process.parent.ppid': { + category: 'process', + description: "Parent process' pid.", + example: 4241, + name: 'process.parent.ppid', + type: 'long', + format: 'string', + }, + 'process.parent.start': { + category: 'process', + description: 'The time the process started.', + example: '2016-05-23T08:05:34.853Z', + name: 'process.parent.start', + type: 'date', + }, + 'process.parent.thread.id': { + category: 'process', + description: 'Thread ID.', + example: 4242, + name: 'process.parent.thread.id', + type: 'long', + format: 'string', + }, + 'process.parent.thread.name': { + category: 'process', + description: 'Thread name.', + example: 'thread-0', + name: 'process.parent.thread.name', + type: 'keyword', + }, + 'process.parent.title': { + category: 'process', + description: + 'Process title. The proctitle, some times the same as process name. Can also be different: for example a browser setting its title to the web page currently opened.', + name: 'process.parent.title', + type: 'keyword', + }, + 'process.parent.uptime': { + category: 'process', + description: 'Seconds the process has been up.', + example: 1325, + name: 'process.parent.uptime', + type: 'long', + }, + 'process.parent.working_directory': { + category: 'process', + description: 'The working directory of the process.', + example: '/home/alice', + name: 'process.parent.working_directory', + type: 'keyword', + }, + 'process.pe.company': { + category: 'process', + description: 'Internal company name of the file, provided at compile-time.', + example: 'Microsoft Corporation', + name: 'process.pe.company', + type: 'keyword', + }, + 'process.pe.description': { + category: 'process', + description: 'Internal description of the file, provided at compile-time.', + example: 'Paint', + name: 'process.pe.description', + type: 'keyword', + }, + 'process.pe.file_version': { + category: 'process', + description: 'Internal version of the file, provided at compile-time.', + example: '6.3.9600.17415', + name: 'process.pe.file_version', + type: 'keyword', + }, + 'process.pe.original_file_name': { + category: 'process', + description: 'Internal name of the file, provided at compile-time.', + example: 'MSPAINT.EXE', + name: 'process.pe.original_file_name', + type: 'keyword', + }, + 'process.pe.product': { + category: 'process', + description: 'Internal product name of the file, provided at compile-time.', + example: 'Microsoft® Windows® Operating System', + name: 'process.pe.product', + type: 'keyword', + }, + 'process.pgid': { + category: 'process', + description: 'Identifier of the group of processes the process belongs to.', + name: 'process.pgid', + type: 'long', + format: 'string', + }, + 'process.pid': { + category: 'process', + description: 'Process id.', + example: 4242, + name: 'process.pid', + type: 'long', + format: 'string', + }, + 'process.ppid': { + category: 'process', + description: "Parent process' pid.", + example: 4241, + name: 'process.ppid', + type: 'long', + format: 'string', + }, + 'process.start': { + category: 'process', + description: 'The time the process started.', + example: '2016-05-23T08:05:34.853Z', + name: 'process.start', + type: 'date', + }, + 'process.thread.id': { + category: 'process', + description: 'Thread ID.', + example: 4242, + name: 'process.thread.id', + type: 'long', + format: 'string', + }, + 'process.thread.name': { + category: 'process', + description: 'Thread name.', + example: 'thread-0', + name: 'process.thread.name', + type: 'keyword', + }, + 'process.title': { + category: 'process', + description: + 'Process title. The proctitle, some times the same as process name. Can also be different: for example a browser setting its title to the web page currently opened.', + name: 'process.title', + type: 'keyword', + }, + 'process.uptime': { + category: 'process', + description: 'Seconds the process has been up.', + example: 1325, + name: 'process.uptime', + type: 'long', + }, + 'process.working_directory': { + category: 'process', + description: 'The working directory of the process.', + example: '/home/alice', + name: 'process.working_directory', + type: 'keyword', + }, + 'registry.data.bytes': { + category: 'registry', + description: + 'Original bytes written with base64 encoding. For Windows registry operations, such as SetValueEx and RegQueryValueEx, this corresponds to the data pointed by `lp_data`. This is optional but provides better recoverability and should be populated for REG_BINARY encoded values.', + example: 'ZQBuAC0AVQBTAAAAZQBuAAAAAAA=', + name: 'registry.data.bytes', + type: 'keyword', + }, + 'registry.data.strings': { + category: 'registry', + description: + 'Content when writing string types. Populated as an array when writing string data to the registry. For single string registry types (REG_SZ, REG_EXPAND_SZ), this should be an array with one string. For sequences of string with REG_MULTI_SZ, this array will be variable length. For numeric data, such as REG_DWORD and REG_QWORD, this should be populated with the decimal representation (e.g `"1"`).', + example: '["C:\\rta\\red_ttp\\bin\\myapp.exe"]', + name: 'registry.data.strings', + type: 'keyword', + }, + 'registry.data.type': { + category: 'registry', + description: 'Standard registry type for encoding contents', + example: 'REG_SZ', + name: 'registry.data.type', + type: 'keyword', + }, + 'registry.hive': { + category: 'registry', + description: 'Abbreviated name for the hive.', + example: 'HKLM', + name: 'registry.hive', + type: 'keyword', + }, + 'registry.key': { + category: 'registry', + description: 'Hive-relative path of keys.', + example: + 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\winword.exe', + name: 'registry.key', + type: 'keyword', + }, + 'registry.path': { + category: 'registry', + description: 'Full path, including hive, key and value', + example: + 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\winword.exe\\Debugger', + name: 'registry.path', + type: 'keyword', + }, + 'registry.value': { + category: 'registry', + description: 'Name of the value written.', + example: 'Debugger', + name: 'registry.value', + type: 'keyword', + }, + 'related.hash': { + category: 'related', + description: + "All the hashes seen on your event. Populating this field, then using it to search for hashes can help in situations where you're unsure what the hash algorithm is (and therefore which key name to search).", + name: 'related.hash', + type: 'keyword', + }, + 'related.ip': { + category: 'related', + description: 'All of the IPs seen on your event.', + name: 'related.ip', + type: 'ip', + }, + 'related.user': { + category: 'related', + description: 'All the user names seen on your event.', + name: 'related.user', + type: 'keyword', + }, + 'rule.author': { + category: 'rule', + description: + 'Name, organization, or pseudonym of the author or authors who created the rule used to generate this event.', + example: '["Star-Lord"]', + name: 'rule.author', + type: 'keyword', + }, + 'rule.category': { + category: 'rule', + description: + 'A categorization value keyword used by the entity using the rule for detection of this event.', + example: 'Attempted Information Leak', + name: 'rule.category', + type: 'keyword', + }, + 'rule.description': { + category: 'rule', + description: 'The description of the rule generating the event.', + example: 'Block requests to public DNS over HTTPS / TLS protocols', + name: 'rule.description', + type: 'keyword', + }, + 'rule.id': { + category: 'rule', + description: + 'A rule ID that is unique within the scope of an agent, observer, or other entity using the rule for detection of this event.', + example: 101, + name: 'rule.id', + type: 'keyword', + }, + 'rule.license': { + category: 'rule', + description: + 'Name of the license under which the rule used to generate this event is made available.', + example: 'Apache 2.0', + name: 'rule.license', + type: 'keyword', + }, + 'rule.name': { + category: 'rule', + description: 'The name of the rule or signature generating the event.', + example: 'BLOCK_DNS_over_TLS', + name: 'rule.name', + type: 'keyword', + }, + 'rule.reference': { + category: 'rule', + description: + "Reference URL to additional information about the rule used to generate this event. The URL can point to the vendor's documentation about the rule. If that's not available, it can also be a link to a more general page describing this type of alert.", + example: 'https://en.wikipedia.org/wiki/DNS_over_TLS', + name: 'rule.reference', + type: 'keyword', + }, + 'rule.ruleset': { + category: 'rule', + description: + 'Name of the ruleset, policy, group, or parent category in which the rule used to generate this event is a member.', + example: 'Standard_Protocol_Filters', + name: 'rule.ruleset', + type: 'keyword', + }, + 'rule.uuid': { + category: 'rule', + description: + 'A rule ID that is unique within the scope of a set or group of agents, observers, or other entities using the rule for detection of this event.', + example: 1100110011, + name: 'rule.uuid', + type: 'keyword', + }, + 'rule.version': { + category: 'rule', + description: 'The version / revision of the rule being used for analysis.', + example: 1.1, + name: 'rule.version', + type: 'keyword', + }, + 'server.address': { + category: 'server', + description: + 'Some event server addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', + name: 'server.address', + type: 'keyword', + }, + 'server.as.number': { + category: 'server', + description: + 'Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet.', + example: 15169, + name: 'server.as.number', + type: 'long', + }, + 'server.as.organization.name': { + category: 'server', + description: 'Organization name.', + example: 'Google LLC', + name: 'server.as.organization.name', + type: 'keyword', + }, + 'server.bytes': { + category: 'server', + description: 'Bytes sent from the server to the client.', + example: 184, + name: 'server.bytes', + type: 'long', + format: 'bytes', + }, + 'server.domain': { + category: 'server', + description: 'Server domain.', + name: 'server.domain', + type: 'keyword', + }, + 'server.geo.city_name': { + category: 'server', + description: 'City name.', + example: 'Montreal', + name: 'server.geo.city_name', + type: 'keyword', + }, + 'server.geo.continent_name': { + category: 'server', + description: 'Name of the continent.', + example: 'North America', + name: 'server.geo.continent_name', + type: 'keyword', + }, + 'server.geo.country_iso_code': { + category: 'server', + description: 'Country ISO code.', + example: 'CA', + name: 'server.geo.country_iso_code', + type: 'keyword', + }, + 'server.geo.country_name': { + category: 'server', + description: 'Country name.', + example: 'Canada', + name: 'server.geo.country_name', + type: 'keyword', + }, + 'server.geo.location': { + category: 'server', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', + name: 'server.geo.location', + type: 'geo_point', + }, + 'server.geo.name': { + category: 'server', + description: + 'User-defined description of a location, at the level of granularity they care about. Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. Not typically used in automated geolocation.', + example: 'boston-dc', + name: 'server.geo.name', + type: 'keyword', + }, + 'server.geo.region_iso_code': { + category: 'server', + description: 'Region ISO code.', + example: 'CA-QC', + name: 'server.geo.region_iso_code', + type: 'keyword', + }, + 'server.geo.region_name': { + category: 'server', + description: 'Region name.', + example: 'Quebec', + name: 'server.geo.region_name', + type: 'keyword', + }, + 'server.ip': { + category: 'server', + description: 'IP address of the server. Can be one or multiple IPv4 or IPv6 addresses.', + name: 'server.ip', + type: 'ip', + }, + 'server.mac': { + category: 'server', + description: 'MAC address of the server.', + name: 'server.mac', + type: 'keyword', + }, + 'server.nat.ip': { + category: 'server', + description: + 'Translated ip of destination based NAT sessions (e.g. internet to private DMZ) Typically used with load balancers, firewalls, or routers.', + name: 'server.nat.ip', + type: 'ip', + }, + 'server.nat.port': { + category: 'server', + description: + 'Translated port of destination based NAT sessions (e.g. internet to private DMZ) Typically used with load balancers, firewalls, or routers.', + name: 'server.nat.port', + type: 'long', + format: 'string', + }, + 'server.packets': { + category: 'server', + description: 'Packets sent from the server to the client.', + example: 12, + name: 'server.packets', + type: 'long', + }, + 'server.port': { + category: 'server', + description: 'Port of the server.', + name: 'server.port', + type: 'long', + format: 'string', + }, + 'server.registered_domain': { + category: 'server', + description: + 'The highest registered server domain, stripped of the subdomain. For example, the registered domain for "foo.google.com" is "google.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', + example: 'google.com', + name: 'server.registered_domain', + type: 'keyword', + }, + 'server.top_level_domain': { + category: 'server', + description: + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', + example: 'co.uk', + name: 'server.top_level_domain', + type: 'keyword', + }, + 'server.user.domain': { + category: 'server', + description: + 'Name of the directory the user is a member of. For example, an LDAP or Active Directory domain name.', + name: 'server.user.domain', + type: 'keyword', + }, + 'server.user.email': { + category: 'server', + description: 'User email address.', + name: 'server.user.email', + type: 'keyword', + }, + 'server.user.full_name': { + category: 'server', + description: "User's full name, if available.", + example: 'Albert Einstein', + name: 'server.user.full_name', + type: 'keyword', + }, + 'server.user.group.domain': { + category: 'server', + description: + 'Name of the directory the group is a member of. For example, an LDAP or Active Directory domain name.', + name: 'server.user.group.domain', + type: 'keyword', + }, + 'server.user.group.id': { + category: 'server', + description: 'Unique identifier for the group on the system/platform.', + name: 'server.user.group.id', + type: 'keyword', + }, + 'server.user.group.name': { + category: 'server', + description: 'Name of the group.', + name: 'server.user.group.name', + type: 'keyword', + }, + 'server.user.hash': { + category: 'server', + description: + 'Unique user hash to correlate information for a user in anonymized form. Useful if `user.id` or `user.name` contain confidential information and cannot be used.', + name: 'server.user.hash', + type: 'keyword', + }, + 'server.user.id': { + category: 'server', + description: 'Unique identifiers of the user.', + name: 'server.user.id', + type: 'keyword', + }, + 'server.user.name': { + category: 'server', + description: 'Short name or login of the user.', + example: 'albert', + name: 'server.user.name', + type: 'keyword', + }, + 'service.ephemeral_id': { + category: 'service', + description: + 'Ephemeral identifier of this service (if one exists). This id normally changes across restarts, but `service.id` does not.', + example: '8a4f500f', + name: 'service.ephemeral_id', + type: 'keyword', + }, + 'service.id': { + category: 'service', + description: + 'Unique identifier of the running service. If the service is comprised of many nodes, the `service.id` should be the same for all nodes. This id should uniquely identify the service. This makes it possible to correlate logs and metrics for one specific service, no matter which particular node emitted the event. Note that if you need to see the events from one specific host of the service, you should filter on that `host.name` or `host.id` instead.', + example: 'd37e5ebfe0ae6c4972dbe9f0174a1637bb8247f6', + name: 'service.id', + type: 'keyword', + }, + 'service.name': { + category: 'service', + description: + 'Name of the service data is collected from. The name of the service is normally user given. This allows for distributed services that run on multiple hosts to correlate the related instances based on the name. In the case of Elasticsearch the `service.name` could contain the cluster name. For Beats the `service.name` is by default a copy of the `service.type` field if no name is specified.', + example: 'elasticsearch-metrics', + name: 'service.name', + type: 'keyword', + }, + 'service.node.name': { + category: 'service', + description: + "Name of a service node. This allows for two nodes of the same service running on the same host to be differentiated. Therefore, `service.node.name` should typically be unique across nodes of a given service. In the case of Elasticsearch, the `service.node.name` could contain the unique node name within the Elasticsearch cluster. In cases where the service doesn't have the concept of a node name, the host name or container name can be used to distinguish running instances that make up this service. If those do not provide uniqueness (e.g. multiple instances of the service running on the same host) - the node name can be manually set.", + example: 'instance-0000000016', + name: 'service.node.name', + type: 'keyword', + }, + 'service.state': { + category: 'service', + description: 'Current state of the service.', + name: 'service.state', + type: 'keyword', + }, + 'service.type': { + category: 'service', + description: + 'The type of the service data is collected from. The type can be used to group and correlate logs and metrics from one service type. Example: If logs or metrics are collected from Elasticsearch, `service.type` would be `elasticsearch`.', + example: 'elasticsearch', + name: 'service.type', + type: 'keyword', + }, + 'service.version': { + category: 'service', + description: + 'Version of the service the data was collected from. This allows to look at a data set only for a specific version of a service.', + example: '3.2.4', + name: 'service.version', + type: 'keyword', + }, + 'source.address': { + category: 'source', + description: + 'Some event source addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', + name: 'source.address', + type: 'keyword', + }, + 'source.as.number': { + category: 'source', + description: + 'Unique number allocated to the autonomous system. The autonomous system number (ASN) uniquely identifies each network on the Internet.', + example: 15169, + name: 'source.as.number', + type: 'long', + }, + 'source.as.organization.name': { + category: 'source', + description: 'Organization name.', + example: 'Google LLC', + name: 'source.as.organization.name', + type: 'keyword', + }, + 'source.bytes': { + category: 'source', + description: 'Bytes sent from the source to the destination.', + example: 184, + name: 'source.bytes', + type: 'long', + format: 'bytes', + }, + 'source.domain': { + category: 'source', + description: 'Source domain.', + name: 'source.domain', + type: 'keyword', + }, + 'source.geo.city_name': { + category: 'source', + description: 'City name.', + example: 'Montreal', + name: 'source.geo.city_name', + type: 'keyword', + }, + 'source.geo.continent_name': { + category: 'source', + description: 'Name of the continent.', + example: 'North America', + name: 'source.geo.continent_name', + type: 'keyword', + }, + 'source.geo.country_iso_code': { + category: 'source', + description: 'Country ISO code.', + example: 'CA', + name: 'source.geo.country_iso_code', + type: 'keyword', + }, + 'source.geo.country_name': { + category: 'source', + description: 'Country name.', + example: 'Canada', + name: 'source.geo.country_name', + type: 'keyword', + }, + 'source.geo.location': { + category: 'source', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', + name: 'source.geo.location', + type: 'geo_point', + }, + 'source.geo.name': { + category: 'source', + description: + 'User-defined description of a location, at the level of granularity they care about. Could be the name of their data centers, the floor number, if this describes a local physical entity, city names. Not typically used in automated geolocation.', + example: 'boston-dc', + name: 'source.geo.name', + type: 'keyword', + }, + 'source.geo.region_iso_code': { + category: 'source', + description: 'Region ISO code.', + example: 'CA-QC', + name: 'source.geo.region_iso_code', + type: 'keyword', + }, + 'source.geo.region_name': { + category: 'source', + description: 'Region name.', + example: 'Quebec', + name: 'source.geo.region_name', + type: 'keyword', + }, + 'source.ip': { + category: 'source', + description: 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', + name: 'source.ip', + type: 'ip', + }, + 'source.mac': { + category: 'source', + description: 'MAC address of the source.', + name: 'source.mac', + type: 'keyword', + }, + 'source.nat.ip': { + category: 'source', + description: + 'Translated ip of source based NAT sessions (e.g. internal client to internet) Typically connections traversing load balancers, firewalls, or routers.', + name: 'source.nat.ip', + type: 'ip', + }, + 'source.nat.port': { + category: 'source', + description: + 'Translated port of source based NAT sessions. (e.g. internal client to internet) Typically used with load balancers, firewalls, or routers.', + name: 'source.nat.port', + type: 'long', + format: 'string', + }, + 'source.packets': { + category: 'source', + description: 'Packets sent from the source to the destination.', + example: 12, + name: 'source.packets', + type: 'long', + }, + 'source.port': { + category: 'source', + description: 'Port of the source.', + name: 'source.port', + type: 'long', + format: 'string', + }, + 'source.registered_domain': { + category: 'source', + description: + 'The highest registered source domain, stripped of the subdomain. For example, the registered domain for "foo.google.com" is "google.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', + example: 'google.com', + name: 'source.registered_domain', + type: 'keyword', + }, + 'source.top_level_domain': { + category: 'source', + description: + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', + example: 'co.uk', + name: 'source.top_level_domain', + type: 'keyword', + }, + 'source.user.domain': { + category: 'source', + description: + 'Name of the directory the user is a member of. For example, an LDAP or Active Directory domain name.', + name: 'source.user.domain', + type: 'keyword', + }, + 'source.user.email': { + category: 'source', + description: 'User email address.', + name: 'source.user.email', + type: 'keyword', + }, + 'source.user.full_name': { + category: 'source', + description: "User's full name, if available.", + example: 'Albert Einstein', + name: 'source.user.full_name', + type: 'keyword', + }, + 'source.user.group.domain': { + category: 'source', + description: + 'Name of the directory the group is a member of. For example, an LDAP or Active Directory domain name.', + name: 'source.user.group.domain', + type: 'keyword', + }, + 'source.user.group.id': { + category: 'source', + description: 'Unique identifier for the group on the system/platform.', + name: 'source.user.group.id', + type: 'keyword', + }, + 'source.user.group.name': { + category: 'source', + description: 'Name of the group.', + name: 'source.user.group.name', + type: 'keyword', + }, + 'source.user.hash': { + category: 'source', + description: + 'Unique user hash to correlate information for a user in anonymized form. Useful if `user.id` or `user.name` contain confidential information and cannot be used.', + name: 'source.user.hash', + type: 'keyword', + }, + 'source.user.id': { + category: 'source', + description: 'Unique identifiers of the user.', + name: 'source.user.id', + type: 'keyword', + }, + 'source.user.name': { + category: 'source', + description: 'Short name or login of the user.', + example: 'albert', + name: 'source.user.name', + type: 'keyword', + }, + 'threat.framework': { + category: 'threat', + description: + 'Name of the threat framework used to further categorize and classify the tactic and technique of the reported threat. Framework classification can be provided by detecting systems, evaluated at ingest time, or retrospectively tagged to events.', + example: 'MITRE ATT&CK', + name: 'threat.framework', + type: 'keyword', + }, + 'threat.tactic.id': { + category: 'threat', + description: + 'The id of tactic used by this threat. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/ )', + example: 'TA0040', + name: 'threat.tactic.id', + type: 'keyword', + }, + 'threat.tactic.name': { + category: 'threat', + description: + 'Name of the type of tactic used by this threat. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/ )', + example: 'impact', + name: 'threat.tactic.name', + type: 'keyword', + }, + 'threat.tactic.reference': { + category: 'threat', + description: + 'The reference url of tactic used by this threat. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/tactics/TA0040/ )', + example: 'https://attack.mitre.org/tactics/TA0040/', + name: 'threat.tactic.reference', + type: 'keyword', + }, + 'threat.technique.id': { + category: 'threat', + description: + 'The id of technique used by this tactic. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/ )', + example: 'T1499', + name: 'threat.technique.id', + type: 'keyword', + }, + 'threat.technique.name': { + category: 'threat', + description: + 'The name of technique used by this tactic. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/ )', + example: 'endpoint denial of service', + name: 'threat.technique.name', + type: 'keyword', + }, + 'threat.technique.reference': { + category: 'threat', + description: + 'The reference url of technique used by this tactic. You can use the Mitre ATT&CK Matrix Tactic categorization, for example. (ex. https://attack.mitre.org/techniques/T1499/ )', + example: 'https://attack.mitre.org/techniques/T1499/', + name: 'threat.technique.reference', + type: 'keyword', + }, + 'tls.cipher': { + category: 'tls', + description: 'String indicating the cipher used during the current connection.', + example: 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256', + name: 'tls.cipher', + type: 'keyword', + }, + 'tls.client.certificate': { + category: 'tls', + description: + 'PEM-encoded stand-alone certificate offered by the client. This is usually mutually-exclusive of `client.certificate_chain` since this value also exists in that list.', + example: 'MII...', + name: 'tls.client.certificate', + type: 'keyword', + }, + 'tls.client.certificate_chain': { + category: 'tls', + description: + 'Array of PEM-encoded certificates that make up the certificate chain offered by the client. This is usually mutually-exclusive of `client.certificate` since that value should be the first certificate in the chain.', + example: '["MII...","MII..."]', + name: 'tls.client.certificate_chain', + type: 'keyword', + }, + 'tls.client.hash.md5': { + category: 'tls', + description: + 'Certificate fingerprint using the MD5 digest of DER-encoded version of certificate offered by the client. For consistency with other hash values, this value should be formatted as an uppercase hash.', + example: '0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC', + name: 'tls.client.hash.md5', + type: 'keyword', + }, + 'tls.client.hash.sha1': { + category: 'tls', + description: + 'Certificate fingerprint using the SHA1 digest of DER-encoded version of certificate offered by the client. For consistency with other hash values, this value should be formatted as an uppercase hash.', + example: '9E393D93138888D288266C2D915214D1D1CCEB2A', + name: 'tls.client.hash.sha1', + type: 'keyword', + }, + 'tls.client.hash.sha256': { + category: 'tls', + description: + 'Certificate fingerprint using the SHA256 digest of DER-encoded version of certificate offered by the client. For consistency with other hash values, this value should be formatted as an uppercase hash.', + example: '0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0', + name: 'tls.client.hash.sha256', + type: 'keyword', + }, + 'tls.client.issuer': { + category: 'tls', + description: + 'Distinguished name of subject of the issuer of the x.509 certificate presented by the client.', + example: 'CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com', + name: 'tls.client.issuer', + type: 'keyword', + }, + 'tls.client.ja3': { + category: 'tls', + description: 'A hash that identifies clients based on how they perform an SSL/TLS handshake.', + example: 'd4e5b18d6b55c71272893221c96ba240', + name: 'tls.client.ja3', + type: 'keyword', + }, + 'tls.client.not_after': { + category: 'tls', + description: 'Date/Time indicating when client certificate is no longer considered valid.', + example: '2021-01-01T00:00:00.000Z', + name: 'tls.client.not_after', + type: 'date', + }, + 'tls.client.not_before': { + category: 'tls', + description: 'Date/Time indicating when client certificate is first considered valid.', + example: '1970-01-01T00:00:00.000Z', + name: 'tls.client.not_before', + type: 'date', + }, + 'tls.client.server_name': { + category: 'tls', + description: + 'Also called an SNI, this tells the server which hostname to which the client is attempting to connect. When this value is available, it should get copied to `destination.domain`.', + example: 'www.elastic.co', + name: 'tls.client.server_name', + type: 'keyword', + }, + 'tls.client.subject': { + category: 'tls', + description: 'Distinguished name of subject of the x.509 certificate presented by the client.', + example: 'CN=myclient, OU=Documentation Team, DC=mydomain, DC=com', + name: 'tls.client.subject', + type: 'keyword', + }, + 'tls.client.supported_ciphers': { + category: 'tls', + description: 'Array of ciphers offered by the client during the client hello.', + example: + '["TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384","..."]', + name: 'tls.client.supported_ciphers', + type: 'keyword', + }, + 'tls.curve': { + category: 'tls', + description: 'String indicating the curve used for the given cipher, when applicable.', + example: 'secp256r1', + name: 'tls.curve', + type: 'keyword', + }, + 'tls.established': { + category: 'tls', + description: + 'Boolean flag indicating if the TLS negotiation was successful and transitioned to an encrypted tunnel.', + name: 'tls.established', + type: 'boolean', + }, + 'tls.next_protocol': { + category: 'tls', + description: + 'String indicating the protocol being tunneled. Per the values in the IANA registry (https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids), this string should be lower case.', + example: 'http/1.1', + name: 'tls.next_protocol', + type: 'keyword', + }, + 'tls.resumed': { + category: 'tls', + description: + 'Boolean flag indicating if this TLS connection was resumed from an existing TLS negotiation.', + name: 'tls.resumed', + type: 'boolean', + }, + 'tls.server.certificate': { + category: 'tls', + description: + 'PEM-encoded stand-alone certificate offered by the server. This is usually mutually-exclusive of `server.certificate_chain` since this value also exists in that list.', + example: 'MII...', + name: 'tls.server.certificate', + type: 'keyword', + }, + 'tls.server.certificate_chain': { + category: 'tls', + description: + 'Array of PEM-encoded certificates that make up the certificate chain offered by the server. This is usually mutually-exclusive of `server.certificate` since that value should be the first certificate in the chain.', + example: '["MII...","MII..."]', + name: 'tls.server.certificate_chain', + type: 'keyword', + }, + 'tls.server.hash.md5': { + category: 'tls', + description: + 'Certificate fingerprint using the MD5 digest of DER-encoded version of certificate offered by the server. For consistency with other hash values, this value should be formatted as an uppercase hash.', + example: '0F76C7F2C55BFD7D8E8B8F4BFBF0C9EC', + name: 'tls.server.hash.md5', + type: 'keyword', + }, + 'tls.server.hash.sha1': { + category: 'tls', + description: + 'Certificate fingerprint using the SHA1 digest of DER-encoded version of certificate offered by the server. For consistency with other hash values, this value should be formatted as an uppercase hash.', + example: '9E393D93138888D288266C2D915214D1D1CCEB2A', + name: 'tls.server.hash.sha1', + type: 'keyword', + }, + 'tls.server.hash.sha256': { + category: 'tls', + description: + 'Certificate fingerprint using the SHA256 digest of DER-encoded version of certificate offered by the server. For consistency with other hash values, this value should be formatted as an uppercase hash.', + example: '0687F666A054EF17A08E2F2162EAB4CBC0D265E1D7875BE74BF3C712CA92DAF0', + name: 'tls.server.hash.sha256', + type: 'keyword', + }, + 'tls.server.issuer': { + category: 'tls', + description: 'Subject of the issuer of the x.509 certificate presented by the server.', + example: 'CN=MyDomain Root CA, OU=Infrastructure Team, DC=mydomain, DC=com', + name: 'tls.server.issuer', + type: 'keyword', + }, + 'tls.server.ja3s': { + category: 'tls', + description: 'A hash that identifies servers based on how they perform an SSL/TLS handshake.', + example: '394441ab65754e2207b1e1b457b3641d', + name: 'tls.server.ja3s', + type: 'keyword', + }, + 'tls.server.not_after': { + category: 'tls', + description: 'Timestamp indicating when server certificate is no longer considered valid.', + example: '2021-01-01T00:00:00.000Z', + name: 'tls.server.not_after', + type: 'date', + }, + 'tls.server.not_before': { + category: 'tls', + description: 'Timestamp indicating when server certificate is first considered valid.', + example: '1970-01-01T00:00:00.000Z', + name: 'tls.server.not_before', + type: 'date', + }, + 'tls.server.subject': { + category: 'tls', + description: 'Subject of the x.509 certificate presented by the server.', + example: 'CN=www.mydomain.com, OU=Infrastructure Team, DC=mydomain, DC=com', + name: 'tls.server.subject', + type: 'keyword', + }, + 'tls.version': { + category: 'tls', + description: 'Numeric part of the version parsed from the original string.', + example: '1.2', + name: 'tls.version', + type: 'keyword', + }, + 'tls.version_protocol': { + category: 'tls', + description: 'Normalized lowercase protocol name parsed from original string.', + example: 'tls', + name: 'tls.version_protocol', + type: 'keyword', + }, + 'tracing.trace.id': { + category: 'tracing', + description: + 'Unique identifier of the trace. A trace groups multiple events like transactions that belong together. For example, a user request handled by multiple inter-connected services.', + example: '4bf92f3577b34da6a3ce929d0e0e4736', + name: 'tracing.trace.id', + type: 'keyword', + }, + 'tracing.transaction.id': { + category: 'tracing', + description: + 'Unique identifier of the transaction. A transaction is the highest level of work measured within a service, such as a request to a server.', + example: '00f067aa0ba902b7', + name: 'tracing.transaction.id', + type: 'keyword', + }, + 'url.domain': { + category: 'url', + description: + 'Domain of the url, such as "www.elastic.co". In some cases a URL may refer to an IP and/or port directly, without a domain name. In this case, the IP address would go to the `domain` field.', + example: 'www.elastic.co', + name: 'url.domain', + type: 'keyword', + }, + 'url.extension': { + category: 'url', + description: + 'The field contains the file extension from the original request url. The file extension is only set if it exists, as not every url has a file extension. The leading period must not be included. For example, the value must be "png", not ".png".', + example: 'png', + name: 'url.extension', + type: 'keyword', + }, + 'url.fragment': { + category: 'url', + description: + 'Portion of the url after the `#`, such as "top". The `#` is not part of the fragment.', + name: 'url.fragment', + type: 'keyword', + }, + 'url.full': { + category: 'url', + description: + 'If full URLs are important to your use case, they should be stored in `url.full`, whether this field is reconstructed or present in the event source.', + example: 'https://www.elastic.co:443/search?q=elasticsearch#top', + name: 'url.full', + type: 'keyword', + }, + 'url.original': { + category: 'url', + description: + 'Unmodified original url as seen in the event source. Note that in network monitoring, the observed URL may be a full URL, whereas in access logs, the URL is often just represented as a path. This field is meant to represent the URL as it was observed, complete or not.', + example: 'https://www.elastic.co:443/search?q=elasticsearch#top or /search?q=elasticsearch', + name: 'url.original', + type: 'keyword', + }, + 'url.password': { + category: 'url', + description: 'Password of the request.', + name: 'url.password', + type: 'keyword', + }, + 'url.path': { + category: 'url', + description: 'Path of the request, such as "/search".', + name: 'url.path', + type: 'keyword', + }, + 'url.port': { + category: 'url', + description: 'Port of the request, such as 443.', + example: 443, + name: 'url.port', + type: 'long', + format: 'string', + }, + 'url.query': { + category: 'url', + description: + 'The query field describes the query string of the request, such as "q=elasticsearch". The `?` is excluded from the query string. If a URL contains no `?`, there is no query field. If there is a `?` but no query, the query field exists with an empty string. The `exists` query can be used to differentiate between the two cases.', + name: 'url.query', + type: 'keyword', + }, + 'url.registered_domain': { + category: 'url', + description: + 'The highest registered url domain, stripped of the subdomain. For example, the registered domain for "foo.google.com" is "google.com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last two labels will not work well for TLDs such as "co.uk".', + example: 'google.com', + name: 'url.registered_domain', + type: 'keyword', + }, + 'url.scheme': { + category: 'url', + description: 'Scheme of the request, such as "https". Note: The `:` is not part of the scheme.', + example: 'https', + name: 'url.scheme', + type: 'keyword', + }, + 'url.top_level_domain': { + category: 'url', + description: + 'The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for google.com is "com". This value can be determined precisely with a list like the public suffix list (http://publicsuffix.org). Trying to approximate this by simply taking the last label will not work well for effective TLDs such as "co.uk".', + example: 'co.uk', + name: 'url.top_level_domain', + type: 'keyword', + }, + 'url.username': { + category: 'url', + description: 'Username of the request.', + name: 'url.username', + type: 'keyword', + }, + 'user.domain': { + category: 'user', + description: + 'Name of the directory the user is a member of. For example, an LDAP or Active Directory domain name.', + name: 'user.domain', + type: 'keyword', + }, + 'user.email': { + category: 'user', + description: 'User email address.', + name: 'user.email', + type: 'keyword', + }, + 'user.full_name': { + category: 'user', + description: "User's full name, if available.", + example: 'Albert Einstein', + name: 'user.full_name', + type: 'keyword', + }, + 'user.group.domain': { + category: 'user', + description: + 'Name of the directory the group is a member of. For example, an LDAP or Active Directory domain name.', + name: 'user.group.domain', + type: 'keyword', + }, + 'user.group.id': { + category: 'user', + description: 'Unique identifier for the group on the system/platform.', + name: 'user.group.id', + type: 'keyword', + }, + 'user.group.name': { + category: 'user', + description: 'Name of the group.', + name: 'user.group.name', + type: 'keyword', + }, + 'user.hash': { + category: 'user', + description: + 'Unique user hash to correlate information for a user in anonymized form. Useful if `user.id` or `user.name` contain confidential information and cannot be used.', + name: 'user.hash', + type: 'keyword', + }, + 'user.id': { + category: 'user', + description: 'Unique identifiers of the user.', + name: 'user.id', + type: 'keyword', + }, + 'user.name': { + category: 'user', + description: 'Short name or login of the user.', + example: 'albert', + name: 'user.name', + type: 'keyword', + }, + 'user_agent.device.name': { + category: 'user_agent', + description: 'Name of the device.', + example: 'iPhone', + name: 'user_agent.device.name', + type: 'keyword', + }, + 'user_agent.name': { + category: 'user_agent', + description: 'Name of the user agent.', + example: 'Safari', + name: 'user_agent.name', + type: 'keyword', + }, + 'user_agent.original': { + category: 'user_agent', + description: 'Unparsed user_agent string.', + example: + 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1', + name: 'user_agent.original', + type: 'keyword', + }, + 'user_agent.os.family': { + category: 'user_agent', + description: 'OS family (such as redhat, debian, freebsd, windows).', + example: 'debian', + name: 'user_agent.os.family', + type: 'keyword', + }, + 'user_agent.os.full': { + category: 'user_agent', + description: 'Operating system name, including the version or code name.', + example: 'Mac OS Mojave', + name: 'user_agent.os.full', + type: 'keyword', + }, + 'user_agent.os.kernel': { + category: 'user_agent', + description: 'Operating system kernel version as a raw string.', + example: '4.4.0-112-generic', + name: 'user_agent.os.kernel', + type: 'keyword', + }, + 'user_agent.os.name': { + category: 'user_agent', + description: 'Operating system name, without the version.', + example: 'Mac OS X', + name: 'user_agent.os.name', + type: 'keyword', + }, + 'user_agent.os.platform': { + category: 'user_agent', + description: 'Operating system platform (such centos, ubuntu, windows).', + example: 'darwin', + name: 'user_agent.os.platform', + type: 'keyword', + }, + 'user_agent.os.version': { + category: 'user_agent', + description: 'Operating system version as a raw string.', + example: '10.14.1', + name: 'user_agent.os.version', + type: 'keyword', + }, + 'user_agent.version': { + category: 'user_agent', + description: 'Version of the user agent.', + example: 12, + name: 'user_agent.version', + type: 'keyword', + }, + 'vlan.id': { + category: 'vlan', + description: 'VLAN ID as reported by the observer.', + example: 10, + name: 'vlan.id', + type: 'keyword', + }, + 'vlan.name': { + category: 'vlan', + description: 'Optional VLAN name as reported by the observer.', + example: 'outside', + name: 'vlan.name', + type: 'keyword', + }, + 'vulnerability.category': { + category: 'vulnerability', + description: + 'The type of system or architecture that the vulnerability affects. These may be platform-specific (for example, Debian or SUSE) or general (for example, Database or Firewall). For example (https://qualysguard.qualys.com/qwebhelp/fo_portal/knowledgebase/vulnerability_categories.htm[Qualys vulnerability categories]) This field must be an array.', + example: '["Firewall"]', + name: 'vulnerability.category', + type: 'keyword', + }, + 'vulnerability.classification': { + category: 'vulnerability', + description: + 'The classification of the vulnerability scoring system. For example (https://www.first.org/cvss/)', + example: 'CVSS', + name: 'vulnerability.classification', + type: 'keyword', + }, + 'vulnerability.description': { + category: 'vulnerability', + description: + 'The description of the vulnerability that provides additional context of the vulnerability. For example (https://cve.mitre.org/about/faqs.html#cve_entry_descriptions_created[Common Vulnerabilities and Exposure CVE description])', + example: 'In macOS before 2.12.6, there is a vulnerability in the RPC...', + name: 'vulnerability.description', + type: 'keyword', + }, + 'vulnerability.enumeration': { + category: 'vulnerability', + description: + 'The type of identifier used for this vulnerability. For example (https://cve.mitre.org/about/)', + example: 'CVE', + name: 'vulnerability.enumeration', + type: 'keyword', + }, + 'vulnerability.id': { + category: 'vulnerability', + description: + 'The identification (ID) is the number portion of a vulnerability entry. It includes a unique identification number for the vulnerability. For example (https://cve.mitre.org/about/faqs.html#what_is_cve_id)[Common Vulnerabilities and Exposure CVE ID]', + example: 'CVE-2019-00001', + name: 'vulnerability.id', + type: 'keyword', + }, + 'vulnerability.reference': { + category: 'vulnerability', + description: + 'A resource that provides additional information, context, and mitigations for the identified vulnerability.', + example: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6111', + name: 'vulnerability.reference', + type: 'keyword', + }, + 'vulnerability.report_id': { + category: 'vulnerability', + description: 'The report or scan identification number.', + example: 20191018.0001, + name: 'vulnerability.report_id', + type: 'keyword', + }, + 'vulnerability.scanner.vendor': { + category: 'vulnerability', + description: 'The name of the vulnerability scanner vendor.', + example: 'Tenable', + name: 'vulnerability.scanner.vendor', + type: 'keyword', + }, + 'vulnerability.score.base': { + category: 'vulnerability', + description: + 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe. Base scores cover an assessment for exploitability metrics (attack vector, complexity, privileges, and user interaction), impact metrics (confidentiality, integrity, and availability), and scope. For example (https://www.first.org/cvss/specification-document)', + example: 5.5, + name: 'vulnerability.score.base', + type: 'float', + }, + 'vulnerability.score.environmental': { + category: 'vulnerability', + description: + 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe. Environmental scores cover an assessment for any modified Base metrics, confidentiality, integrity, and availability requirements. For example (https://www.first.org/cvss/specification-document)', + example: 5.5, + name: 'vulnerability.score.environmental', + type: 'float', + }, + 'vulnerability.score.temporal': { + category: 'vulnerability', + description: + 'Scores can range from 0.0 to 10.0, with 10.0 being the most severe. Temporal scores cover an assessment for code maturity, remediation level, and confidence. For example (https://www.first.org/cvss/specification-document)', + name: 'vulnerability.score.temporal', + type: 'float', + }, + 'vulnerability.score.version': { + category: 'vulnerability', + description: + 'The National Vulnerability Database (NVD) provides qualitative severity rankings of "Low", "Medium", and "High" for CVSS v2.0 base score ranges in addition to the severity ratings for CVSS v3.0 as they are defined in the CVSS v3.0 specification. CVSS is owned and managed by FIRST.Org, Inc. (FIRST), a US-based non-profit organization, whose mission is to help computer security incident response teams across the world. For example (https://nvd.nist.gov/vuln-metrics/cvss)', + example: 2, + name: 'vulnerability.score.version', + type: 'keyword', + }, + 'vulnerability.severity': { + category: 'vulnerability', + description: + 'The severity of the vulnerability can help with metrics and internal prioritization regarding remediation. For example (https://nvd.nist.gov/vuln-metrics/cvss)', + example: 'Critical', + name: 'vulnerability.severity', + type: 'keyword', + }, + 'agent.hostname': { + category: 'agent', + description: + 'Deprecated - use agent.name or agent.id to identify an agent. Hostname of the agent. ', + name: 'agent.hostname', + type: 'keyword', + }, + 'beat.timezone': { + category: 'beat', + name: 'beat.timezone', + type: 'alias', + }, + fields: { + category: 'base', + description: 'Contains user configurable fields. ', + name: 'fields', + type: 'object', + }, + 'beat.name': { + category: 'beat', + name: 'beat.name', + type: 'alias', + }, + 'beat.hostname': { + category: 'beat', + name: 'beat.hostname', + type: 'alias', + }, + 'timeseries.instance': { + category: 'timeseries', + description: 'Time series instance id', + name: 'timeseries.instance', + type: 'keyword', + }, + 'cloud.project.id': { + category: 'cloud', + description: 'Name of the project in Google Cloud. ', + example: 'project-x', + name: 'cloud.project.id', + }, + 'cloud.image.id': { + category: 'cloud', + description: 'Image ID for the cloud instance. ', + example: 'ami-abcd1234', + name: 'cloud.image.id', + }, + 'meta.cloud.provider': { + category: 'meta', + name: 'meta.cloud.provider', + type: 'alias', + }, + 'meta.cloud.instance_id': { + category: 'meta', + name: 'meta.cloud.instance_id', + type: 'alias', + }, + 'meta.cloud.instance_name': { + category: 'meta', + name: 'meta.cloud.instance_name', + type: 'alias', + }, + 'meta.cloud.machine_type': { + category: 'meta', + name: 'meta.cloud.machine_type', + type: 'alias', + }, + 'meta.cloud.availability_zone': { + category: 'meta', + name: 'meta.cloud.availability_zone', + type: 'alias', + }, + 'meta.cloud.project_id': { + category: 'meta', + name: 'meta.cloud.project_id', + type: 'alias', + }, + 'meta.cloud.region': { + category: 'meta', + name: 'meta.cloud.region', + type: 'alias', + }, + 'docker.container.id': { + category: 'docker', + name: 'docker.container.id', + type: 'alias', + }, + 'docker.container.image': { + category: 'docker', + name: 'docker.container.image', + type: 'alias', + }, + 'docker.container.name': { + category: 'docker', + name: 'docker.container.name', + type: 'alias', + }, + 'docker.container.labels': { + category: 'docker', + description: 'Image labels. ', + name: 'docker.container.labels', + type: 'object', + }, + 'host.containerized': { + category: 'host', + description: 'If the host is a container. ', + name: 'host.containerized', + type: 'boolean', + }, + 'host.os.build': { + category: 'host', + description: 'OS build information. ', + example: '18D109', + name: 'host.os.build', + type: 'keyword', + }, + 'host.os.codename': { + category: 'host', + description: 'OS codename, if any. ', + example: 'stretch', + name: 'host.os.codename', + type: 'keyword', + }, + 'kubernetes.pod.name': { + category: 'kubernetes', + description: 'Kubernetes pod name ', + name: 'kubernetes.pod.name', + type: 'keyword', + }, + 'kubernetes.pod.uid': { + category: 'kubernetes', + description: 'Kubernetes Pod UID ', + name: 'kubernetes.pod.uid', + type: 'keyword', + }, + 'kubernetes.namespace': { + category: 'kubernetes', + description: 'Kubernetes namespace ', + name: 'kubernetes.namespace', + type: 'keyword', + }, + 'kubernetes.node.name': { + category: 'kubernetes', + description: 'Kubernetes node name ', + name: 'kubernetes.node.name', + type: 'keyword', + }, + 'kubernetes.labels.*': { + category: 'kubernetes', + description: 'Kubernetes labels map ', + name: 'kubernetes.labels.*', + type: 'object', + }, + 'kubernetes.annotations.*': { + category: 'kubernetes', + description: 'Kubernetes annotations map ', + name: 'kubernetes.annotations.*', + type: 'object', + }, + 'kubernetes.replicaset.name': { + category: 'kubernetes', + description: 'Kubernetes replicaset name ', + name: 'kubernetes.replicaset.name', + type: 'keyword', + }, + 'kubernetes.deployment.name': { + category: 'kubernetes', + description: 'Kubernetes deployment name ', + name: 'kubernetes.deployment.name', + type: 'keyword', + }, + 'kubernetes.statefulset.name': { + category: 'kubernetes', + description: 'Kubernetes statefulset name ', + name: 'kubernetes.statefulset.name', + type: 'keyword', + }, + 'kubernetes.container.name': { + category: 'kubernetes', + description: 'Kubernetes container name ', + name: 'kubernetes.container.name', + type: 'keyword', + }, + 'kubernetes.container.image': { + category: 'kubernetes', + description: 'Kubernetes container image ', + name: 'kubernetes.container.image', + type: 'keyword', + }, + 'process.exe': { + category: 'process', + name: 'process.exe', + type: 'alias', + }, + 'jolokia.agent.version': { + category: 'jolokia', + description: 'Version number of jolokia agent. ', + name: 'jolokia.agent.version', + type: 'keyword', + }, + 'jolokia.agent.id': { + category: 'jolokia', + description: + 'Each agent has a unique id which can be either provided during startup of the agent in form of a configuration parameter or being autodetected. If autodected, the id has several parts: The IP, the process id, hashcode of the agent and its type. ', + name: 'jolokia.agent.id', + type: 'keyword', + }, + 'jolokia.server.product': { + category: 'jolokia', + description: 'The container product if detected. ', + name: 'jolokia.server.product', + type: 'keyword', + }, + 'jolokia.server.version': { + category: 'jolokia', + description: "The container's version (if detected). ", + name: 'jolokia.server.version', + type: 'keyword', + }, + 'jolokia.server.vendor': { + category: 'jolokia', + description: 'The vendor of the container the agent is running in. ', + name: 'jolokia.server.vendor', + type: 'keyword', + }, + 'jolokia.url': { + category: 'jolokia', + description: 'The URL how this agent can be contacted. ', + name: 'jolokia.url', + type: 'keyword', + }, + 'jolokia.secured': { + category: 'jolokia', + description: 'Whether the agent was configured for authentication or not. ', + name: 'jolokia.secured', + type: 'boolean', + }, + 'file.setuid': { + category: 'file', + description: 'Set if the file has the `setuid` bit set. Omitted otherwise.', + example: 'true', + name: 'file.setuid', + type: 'boolean', + }, + 'file.setgid': { + category: 'file', + description: 'Set if the file has the `setgid` bit set. Omitted otherwise.', + example: 'true', + name: 'file.setgid', + type: 'boolean', + }, + 'file.origin': { + category: 'file', + description: + 'An array of strings describing a possible external origin for this file. For example, the URL it was downloaded from. Only supported in macOS, via the kMDItemWhereFroms attribute. Omitted if origin information is not available. ', + name: 'file.origin', + type: 'keyword', + }, + 'file.selinux.user': { + category: 'file', + description: 'The owner of the object.', + name: 'file.selinux.user', + type: 'keyword', + }, + 'file.selinux.role': { + category: 'file', + description: "The object's SELinux role.", + name: 'file.selinux.role', + type: 'keyword', + }, + 'file.selinux.domain': { + category: 'file', + description: "The object's SELinux domain or type.", + name: 'file.selinux.domain', + type: 'keyword', + }, + 'file.selinux.level': { + category: 'file', + description: "The object's SELinux level.", + example: 's0', + name: 'file.selinux.level', + type: 'keyword', + }, + 'user.audit.id': { + category: 'user', + description: 'Audit user ID.', + name: 'user.audit.id', + type: 'keyword', + }, + 'user.audit.name': { + category: 'user', + description: 'Audit user name.', + name: 'user.audit.name', + type: 'keyword', + }, + 'user.effective.id': { + category: 'user', + description: 'Effective user ID.', + name: 'user.effective.id', + type: 'keyword', + }, + 'user.effective.name': { + category: 'user', + description: 'Effective user name.', + name: 'user.effective.name', + type: 'keyword', + }, + 'user.effective.group.id': { + category: 'user', + description: 'Effective group ID.', + name: 'user.effective.group.id', + type: 'keyword', + }, + 'user.effective.group.name': { + category: 'user', + description: 'Effective group name.', + name: 'user.effective.group.name', + type: 'keyword', + }, + 'user.filesystem.id': { + category: 'user', + description: 'Filesystem user ID.', + name: 'user.filesystem.id', + type: 'keyword', + }, + 'user.filesystem.name': { + category: 'user', + description: 'Filesystem user name.', + name: 'user.filesystem.name', + type: 'keyword', + }, + 'user.filesystem.group.id': { + category: 'user', + description: 'Filesystem group ID.', + name: 'user.filesystem.group.id', + type: 'keyword', + }, + 'user.filesystem.group.name': { + category: 'user', + description: 'Filesystem group name.', + name: 'user.filesystem.group.name', + type: 'keyword', + }, + 'user.saved.id': { + category: 'user', + description: 'Saved user ID.', + name: 'user.saved.id', + type: 'keyword', + }, + 'user.saved.name': { + category: 'user', + description: 'Saved user name.', + name: 'user.saved.name', + type: 'keyword', + }, + 'user.saved.group.id': { + category: 'user', + description: 'Saved group ID.', + name: 'user.saved.group.id', + type: 'keyword', + }, + 'user.saved.group.name': { + category: 'user', + description: 'Saved group name.', + name: 'user.saved.group.name', + type: 'keyword', + }, + 'user.auid': { + category: 'user', + name: 'user.auid', + type: 'alias', + }, + 'user.uid': { + category: 'user', + name: 'user.uid', + type: 'alias', + }, + 'user.euid': { + category: 'user', + name: 'user.euid', + type: 'alias', + }, + 'user.fsuid': { + category: 'user', + name: 'user.fsuid', + type: 'alias', + }, + 'user.suid': { + category: 'user', + name: 'user.suid', + type: 'alias', + }, + 'user.gid': { + category: 'user', + name: 'user.gid', + type: 'alias', + }, + 'user.egid': { + category: 'user', + name: 'user.egid', + type: 'alias', + }, + 'user.sgid': { + category: 'user', + name: 'user.sgid', + type: 'alias', + }, + 'user.fsgid': { + category: 'user', + name: 'user.fsgid', + type: 'alias', + }, + 'user.name_map.auid': { + category: 'user', + name: 'user.name_map.auid', + type: 'alias', + }, + 'user.name_map.uid': { + category: 'user', + name: 'user.name_map.uid', + type: 'alias', + }, + 'user.name_map.euid': { + category: 'user', + name: 'user.name_map.euid', + type: 'alias', + }, + 'user.name_map.fsuid': { + category: 'user', + name: 'user.name_map.fsuid', + type: 'alias', + }, + 'user.name_map.suid': { + category: 'user', + name: 'user.name_map.suid', + type: 'alias', + }, + 'user.name_map.gid': { + category: 'user', + name: 'user.name_map.gid', + type: 'alias', + }, + 'user.name_map.egid': { + category: 'user', + name: 'user.name_map.egid', + type: 'alias', + }, + 'user.name_map.sgid': { + category: 'user', + name: 'user.name_map.sgid', + type: 'alias', + }, + 'user.name_map.fsgid': { + category: 'user', + name: 'user.name_map.fsgid', + type: 'alias', + }, + 'user.selinux.user': { + category: 'user', + description: 'account submitted for authentication', + name: 'user.selinux.user', + type: 'keyword', + }, + 'user.selinux.role': { + category: 'user', + description: "user's SELinux role", + name: 'user.selinux.role', + type: 'keyword', + }, + 'user.selinux.domain': { + category: 'user', + description: "The actor's SELinux domain or type.", + name: 'user.selinux.domain', + type: 'keyword', + }, + 'user.selinux.level': { + category: 'user', + description: "The actor's SELinux level.", + example: 's0', + name: 'user.selinux.level', + type: 'keyword', + }, + 'user.selinux.category': { + category: 'user', + description: "The actor's SELinux category or compartments.", + name: 'user.selinux.category', + type: 'keyword', + }, + 'process.cwd': { + category: 'process', + description: 'The current working directory.', + name: 'process.cwd', + type: 'alias', + }, + 'source.path': { + category: 'source', + description: 'This is the path associated with a unix socket.', + name: 'source.path', + type: 'keyword', + }, + 'destination.path': { + category: 'destination', + description: 'This is the path associated with a unix socket.', + name: 'destination.path', + type: 'keyword', + }, + 'auditd.message_type': { + category: 'auditd', + description: 'The audit message type (e.g. syscall or apparmor_denied). ', + example: 'syscall', + name: 'auditd.message_type', + type: 'keyword', + }, + 'auditd.sequence': { + category: 'auditd', + description: + 'The sequence number of the event as assigned by the kernel. Sequence numbers are stored as a uint32 in the kernel and can rollover. ', + name: 'auditd.sequence', + type: 'long', + }, + 'auditd.session': { + category: 'auditd', + description: + 'The session ID assigned to a login. All events related to a login session will have the same value. ', + name: 'auditd.session', + type: 'keyword', + }, + 'auditd.result': { + category: 'auditd', + description: 'The result of the audited operation (success/fail).', + example: 'success or fail', + name: 'auditd.result', + type: 'keyword', + }, + 'auditd.summary.actor.primary': { + category: 'auditd', + description: + "The primary identity of the actor. This is the actor's original login ID. It will not change even if the user changes to another account. ", + name: 'auditd.summary.actor.primary', + type: 'keyword', + }, + 'auditd.summary.actor.secondary': { + category: 'auditd', + description: + 'The secondary identity of the actor. This is typically the same as the primary, except for when the user has used `su`.', + name: 'auditd.summary.actor.secondary', + type: 'keyword', + }, + 'auditd.summary.object.type': { + category: 'auditd', + description: 'A description of the what the "thing" is (e.g. file, socket, user-session). ', + name: 'auditd.summary.object.type', + type: 'keyword', + }, + 'auditd.summary.object.primary': { + category: 'auditd', + description: '', + name: 'auditd.summary.object.primary', + type: 'keyword', + }, + 'auditd.summary.object.secondary': { + category: 'auditd', + description: '', + name: 'auditd.summary.object.secondary', + type: 'keyword', + }, + 'auditd.summary.how': { + category: 'auditd', + description: + 'This describes how the action was performed. Usually this is the exe or command that was being executed that triggered the event. ', + name: 'auditd.summary.how', + type: 'keyword', + }, + 'auditd.paths.inode': { + category: 'auditd', + description: 'inode number', + name: 'auditd.paths.inode', + type: 'keyword', + }, + 'auditd.paths.dev': { + category: 'auditd', + description: 'device name as found in /dev', + name: 'auditd.paths.dev', + type: 'keyword', + }, + 'auditd.paths.obj_user': { + category: 'auditd', + description: '', + name: 'auditd.paths.obj_user', + type: 'keyword', + }, + 'auditd.paths.obj_role': { + category: 'auditd', + description: '', + name: 'auditd.paths.obj_role', + type: 'keyword', + }, + 'auditd.paths.obj_domain': { + category: 'auditd', + description: '', + name: 'auditd.paths.obj_domain', + type: 'keyword', + }, + 'auditd.paths.obj_level': { + category: 'auditd', + description: '', + name: 'auditd.paths.obj_level', + type: 'keyword', + }, + 'auditd.paths.objtype': { + category: 'auditd', + description: '', + name: 'auditd.paths.objtype', + type: 'keyword', + }, + 'auditd.paths.ouid': { + category: 'auditd', + description: 'file owner user ID', + name: 'auditd.paths.ouid', + type: 'keyword', + }, + 'auditd.paths.rdev': { + category: 'auditd', + description: 'the device identifier (special files only)', + name: 'auditd.paths.rdev', + type: 'keyword', + }, + 'auditd.paths.nametype': { + category: 'auditd', + description: 'kind of file operation being referenced', + name: 'auditd.paths.nametype', + type: 'keyword', + }, + 'auditd.paths.ogid': { + category: 'auditd', + description: 'file owner group ID', + name: 'auditd.paths.ogid', + type: 'keyword', + }, + 'auditd.paths.item': { + category: 'auditd', + description: 'which item is being recorded', + name: 'auditd.paths.item', + type: 'keyword', + }, + 'auditd.paths.mode': { + category: 'auditd', + description: 'mode flags on a file', + name: 'auditd.paths.mode', + type: 'keyword', + }, + 'auditd.paths.name': { + category: 'auditd', + description: 'file name in avcs', + name: 'auditd.paths.name', + type: 'keyword', + }, + 'auditd.data.action': { + category: 'auditd', + description: 'netfilter packet disposition', + name: 'auditd.data.action', + type: 'keyword', + }, + 'auditd.data.minor': { + category: 'auditd', + description: 'device minor number', + name: 'auditd.data.minor', + type: 'keyword', + }, + 'auditd.data.acct': { + category: 'auditd', + description: "a user's account name", + name: 'auditd.data.acct', + type: 'keyword', + }, + 'auditd.data.addr': { + category: 'auditd', + description: 'the remote address that the user is connecting from', + name: 'auditd.data.addr', + type: 'keyword', + }, + 'auditd.data.cipher': { + category: 'auditd', + description: 'name of crypto cipher selected', + name: 'auditd.data.cipher', + type: 'keyword', + }, + 'auditd.data.id': { + category: 'auditd', + description: 'during account changes', + name: 'auditd.data.id', + type: 'keyword', + }, + 'auditd.data.entries': { + category: 'auditd', + description: 'number of entries in the netfilter table', + name: 'auditd.data.entries', + type: 'keyword', + }, + 'auditd.data.kind': { + category: 'auditd', + description: 'server or client in crypto operation', + name: 'auditd.data.kind', + type: 'keyword', + }, + 'auditd.data.ksize': { + category: 'auditd', + description: 'key size for crypto operation', + name: 'auditd.data.ksize', + type: 'keyword', + }, + 'auditd.data.spid': { + category: 'auditd', + description: 'sent process ID', + name: 'auditd.data.spid', + type: 'keyword', + }, + 'auditd.data.arch': { + category: 'auditd', + description: 'the elf architecture flags', + name: 'auditd.data.arch', + type: 'keyword', + }, + 'auditd.data.argc': { + category: 'auditd', + description: 'the number of arguments to an execve syscall', + name: 'auditd.data.argc', + type: 'keyword', + }, + 'auditd.data.major': { + category: 'auditd', + description: 'device major number', + name: 'auditd.data.major', + type: 'keyword', + }, + 'auditd.data.unit': { + category: 'auditd', + description: 'systemd unit', + name: 'auditd.data.unit', + type: 'keyword', + }, + 'auditd.data.table': { + category: 'auditd', + description: 'netfilter table name', + name: 'auditd.data.table', + type: 'keyword', + }, + 'auditd.data.terminal': { + category: 'auditd', + description: 'terminal name the user is running programs on', + name: 'auditd.data.terminal', + type: 'keyword', + }, + 'auditd.data.grantors': { + category: 'auditd', + description: 'pam modules approving the action', + name: 'auditd.data.grantors', + type: 'keyword', + }, + 'auditd.data.direction': { + category: 'auditd', + description: 'direction of crypto operation', + name: 'auditd.data.direction', + type: 'keyword', + }, + 'auditd.data.op': { + category: 'auditd', + description: 'the operation being performed that is audited', + name: 'auditd.data.op', + type: 'keyword', + }, + 'auditd.data.tty': { + category: 'auditd', + description: 'tty udevice the user is running programs on', + name: 'auditd.data.tty', + type: 'keyword', + }, + 'auditd.data.syscall': { + category: 'auditd', + description: 'syscall number in effect when the event occurred', + name: 'auditd.data.syscall', + type: 'keyword', + }, + 'auditd.data.data': { + category: 'auditd', + description: 'TTY text', + name: 'auditd.data.data', + type: 'keyword', + }, + 'auditd.data.family': { + category: 'auditd', + description: 'netfilter protocol', + name: 'auditd.data.family', + type: 'keyword', + }, + 'auditd.data.mac': { + category: 'auditd', + description: 'crypto MAC algorithm selected', + name: 'auditd.data.mac', + type: 'keyword', + }, + 'auditd.data.pfs': { + category: 'auditd', + description: 'perfect forward secrecy method', + name: 'auditd.data.pfs', + type: 'keyword', + }, + 'auditd.data.items': { + category: 'auditd', + description: 'the number of path records in the event', + name: 'auditd.data.items', + type: 'keyword', + }, + 'auditd.data.a0': { + category: 'auditd', + description: '', + name: 'auditd.data.a0', + type: 'keyword', + }, + 'auditd.data.a1': { + category: 'auditd', + description: '', + name: 'auditd.data.a1', + type: 'keyword', + }, + 'auditd.data.a2': { + category: 'auditd', + description: '', + name: 'auditd.data.a2', + type: 'keyword', + }, + 'auditd.data.a3': { + category: 'auditd', + description: '', + name: 'auditd.data.a3', + type: 'keyword', + }, + 'auditd.data.hostname': { + category: 'auditd', + description: 'the hostname that the user is connecting from', + name: 'auditd.data.hostname', + type: 'keyword', + }, + 'auditd.data.lport': { + category: 'auditd', + description: 'local network port', + name: 'auditd.data.lport', + type: 'keyword', + }, + 'auditd.data.rport': { + category: 'auditd', + description: 'remote port number', + name: 'auditd.data.rport', + type: 'keyword', + }, + 'auditd.data.exit': { + category: 'auditd', + description: 'syscall exit code', + name: 'auditd.data.exit', + type: 'keyword', + }, + 'auditd.data.fp': { + category: 'auditd', + description: 'crypto key finger print', + name: 'auditd.data.fp', + type: 'keyword', + }, + 'auditd.data.laddr': { + category: 'auditd', + description: 'local network address', + name: 'auditd.data.laddr', + type: 'keyword', + }, + 'auditd.data.sport': { + category: 'auditd', + description: 'local port number', + name: 'auditd.data.sport', + type: 'keyword', + }, + 'auditd.data.capability': { + category: 'auditd', + description: 'posix capabilities', + name: 'auditd.data.capability', + type: 'keyword', + }, + 'auditd.data.nargs': { + category: 'auditd', + description: 'the number of arguments to a socket call', + name: 'auditd.data.nargs', + type: 'keyword', + }, + 'auditd.data.new-enabled': { + category: 'auditd', + description: 'new TTY audit enabled setting', + name: 'auditd.data.new-enabled', + type: 'keyword', + }, + 'auditd.data.audit_backlog_limit': { + category: 'auditd', + description: "audit system's backlog queue size", + name: 'auditd.data.audit_backlog_limit', + type: 'keyword', + }, + 'auditd.data.dir': { + category: 'auditd', + description: 'directory name', + name: 'auditd.data.dir', + type: 'keyword', + }, + 'auditd.data.cap_pe': { + category: 'auditd', + description: 'process effective capability map', + name: 'auditd.data.cap_pe', + type: 'keyword', + }, + 'auditd.data.model': { + category: 'auditd', + description: 'security model being used for virt', + name: 'auditd.data.model', + type: 'keyword', + }, + 'auditd.data.new_pp': { + category: 'auditd', + description: 'new process permitted capability map', + name: 'auditd.data.new_pp', + type: 'keyword', + }, + 'auditd.data.old-enabled': { + category: 'auditd', + description: 'present TTY audit enabled setting', + name: 'auditd.data.old-enabled', + type: 'keyword', + }, + 'auditd.data.oauid': { + category: 'auditd', + description: "object's login user ID", + name: 'auditd.data.oauid', + type: 'keyword', + }, + 'auditd.data.old': { + category: 'auditd', + description: 'old value', + name: 'auditd.data.old', + type: 'keyword', + }, + 'auditd.data.banners': { + category: 'auditd', + description: 'banners used on printed page', + name: 'auditd.data.banners', + type: 'keyword', + }, + 'auditd.data.feature': { + category: 'auditd', + description: 'kernel feature being changed', + name: 'auditd.data.feature', + type: 'keyword', + }, + 'auditd.data.vm-ctx': { + category: 'auditd', + description: "the vm's context string", + name: 'auditd.data.vm-ctx', + type: 'keyword', + }, + 'auditd.data.opid': { + category: 'auditd', + description: "object's process ID", + name: 'auditd.data.opid', + type: 'keyword', + }, + 'auditd.data.seperms': { + category: 'auditd', + description: 'SELinux permissions being used', + name: 'auditd.data.seperms', + type: 'keyword', + }, + 'auditd.data.seresult': { + category: 'auditd', + description: 'SELinux AVC decision granted/denied', + name: 'auditd.data.seresult', + type: 'keyword', + }, + 'auditd.data.new-rng': { + category: 'auditd', + description: 'device name of rng being added from a vm', + name: 'auditd.data.new-rng', + type: 'keyword', + }, + 'auditd.data.old-net': { + category: 'auditd', + description: 'present MAC address assigned to vm', + name: 'auditd.data.old-net', + type: 'keyword', + }, + 'auditd.data.sigev_signo': { + category: 'auditd', + description: 'signal number', + name: 'auditd.data.sigev_signo', + type: 'keyword', + }, + 'auditd.data.ino': { + category: 'auditd', + description: 'inode number', + name: 'auditd.data.ino', + type: 'keyword', + }, + 'auditd.data.old_enforcing': { + category: 'auditd', + description: 'old MAC enforcement status', + name: 'auditd.data.old_enforcing', + type: 'keyword', + }, + 'auditd.data.old-vcpu': { + category: 'auditd', + description: 'present number of CPU cores', + name: 'auditd.data.old-vcpu', + type: 'keyword', + }, + 'auditd.data.range': { + category: 'auditd', + description: "user's SE Linux range", + name: 'auditd.data.range', + type: 'keyword', + }, + 'auditd.data.res': { + category: 'auditd', + description: 'result of the audited operation(success/fail)', + name: 'auditd.data.res', + type: 'keyword', + }, + 'auditd.data.added': { + category: 'auditd', + description: 'number of new files detected', + name: 'auditd.data.added', + type: 'keyword', + }, + 'auditd.data.fam': { + category: 'auditd', + description: 'socket address family', + name: 'auditd.data.fam', + type: 'keyword', + }, + 'auditd.data.nlnk-pid': { + category: 'auditd', + description: 'pid of netlink packet sender', + name: 'auditd.data.nlnk-pid', + type: 'keyword', + }, + 'auditd.data.subj': { + category: 'auditd', + description: "lspp subject's context string", + name: 'auditd.data.subj', + type: 'keyword', + }, + 'auditd.data.a[0-3]': { + category: 'auditd', + description: 'the arguments to a syscall', + name: 'auditd.data.a[0-3]', + type: 'keyword', + }, + 'auditd.data.cgroup': { + category: 'auditd', + description: 'path to cgroup in sysfs', + name: 'auditd.data.cgroup', + type: 'keyword', + }, + 'auditd.data.kernel': { + category: 'auditd', + description: "kernel's version number", + name: 'auditd.data.kernel', + type: 'keyword', + }, + 'auditd.data.ocomm': { + category: 'auditd', + description: "object's command line name", + name: 'auditd.data.ocomm', + type: 'keyword', + }, + 'auditd.data.new-net': { + category: 'auditd', + description: 'MAC address being assigned to vm', + name: 'auditd.data.new-net', + type: 'keyword', + }, + 'auditd.data.permissive': { + category: 'auditd', + description: 'SELinux is in permissive mode', + name: 'auditd.data.permissive', + type: 'keyword', + }, + 'auditd.data.class': { + category: 'auditd', + description: 'resource class assigned to vm', + name: 'auditd.data.class', + type: 'keyword', + }, + 'auditd.data.compat': { + category: 'auditd', + description: 'is_compat_task result', + name: 'auditd.data.compat', + type: 'keyword', + }, + 'auditd.data.fi': { + category: 'auditd', + description: 'file assigned inherited capability map', + name: 'auditd.data.fi', + type: 'keyword', + }, + 'auditd.data.changed': { + category: 'auditd', + description: 'number of changed files', + name: 'auditd.data.changed', + type: 'keyword', + }, + 'auditd.data.msg': { + category: 'auditd', + description: 'the payload of the audit record', + name: 'auditd.data.msg', + type: 'keyword', + }, + 'auditd.data.dport': { + category: 'auditd', + description: 'remote port number', + name: 'auditd.data.dport', + type: 'keyword', + }, + 'auditd.data.new-seuser': { + category: 'auditd', + description: 'new SELinux user', + name: 'auditd.data.new-seuser', + type: 'keyword', + }, + 'auditd.data.invalid_context': { + category: 'auditd', + description: 'SELinux context', + name: 'auditd.data.invalid_context', + type: 'keyword', + }, + 'auditd.data.dmac': { + category: 'auditd', + description: 'remote MAC address', + name: 'auditd.data.dmac', + type: 'keyword', + }, + 'auditd.data.ipx-net': { + category: 'auditd', + description: 'IPX network number', + name: 'auditd.data.ipx-net', + type: 'keyword', + }, + 'auditd.data.iuid': { + category: 'auditd', + description: "ipc object's user ID", + name: 'auditd.data.iuid', + type: 'keyword', + }, + 'auditd.data.macproto': { + category: 'auditd', + description: 'ethernet packet type ID field', + name: 'auditd.data.macproto', + type: 'keyword', + }, + 'auditd.data.obj': { + category: 'auditd', + description: 'lspp object context string', + name: 'auditd.data.obj', + type: 'keyword', + }, + 'auditd.data.ipid': { + category: 'auditd', + description: 'IP datagram fragment identifier', + name: 'auditd.data.ipid', + type: 'keyword', + }, + 'auditd.data.new-fs': { + category: 'auditd', + description: 'file system being added to vm', + name: 'auditd.data.new-fs', + type: 'keyword', + }, + 'auditd.data.vm-pid': { + category: 'auditd', + description: "vm's process ID", + name: 'auditd.data.vm-pid', + type: 'keyword', + }, + 'auditd.data.cap_pi': { + category: 'auditd', + description: 'process inherited capability map', + name: 'auditd.data.cap_pi', + type: 'keyword', + }, + 'auditd.data.old-auid': { + category: 'auditd', + description: 'previous auid value', + name: 'auditd.data.old-auid', + type: 'keyword', + }, + 'auditd.data.oses': { + category: 'auditd', + description: "object's session ID", + name: 'auditd.data.oses', + type: 'keyword', + }, + 'auditd.data.fd': { + category: 'auditd', + description: 'file descriptor number', + name: 'auditd.data.fd', + type: 'keyword', + }, + 'auditd.data.igid': { + category: 'auditd', + description: "ipc object's group ID", + name: 'auditd.data.igid', + type: 'keyword', + }, + 'auditd.data.new-disk': { + category: 'auditd', + description: 'disk being added to vm', + name: 'auditd.data.new-disk', + type: 'keyword', + }, + 'auditd.data.parent': { + category: 'auditd', + description: 'the inode number of the parent file', + name: 'auditd.data.parent', + type: 'keyword', + }, + 'auditd.data.len': { + category: 'auditd', + description: 'length', + name: 'auditd.data.len', + type: 'keyword', + }, + 'auditd.data.oflag': { + category: 'auditd', + description: 'open syscall flags', + name: 'auditd.data.oflag', + type: 'keyword', + }, + 'auditd.data.uuid': { + category: 'auditd', + description: 'a UUID', + name: 'auditd.data.uuid', + type: 'keyword', + }, + 'auditd.data.code': { + category: 'auditd', + description: 'seccomp action code', + name: 'auditd.data.code', + type: 'keyword', + }, + 'auditd.data.nlnk-grp': { + category: 'auditd', + description: 'netlink group number', + name: 'auditd.data.nlnk-grp', + type: 'keyword', + }, + 'auditd.data.cap_fp': { + category: 'auditd', + description: 'file permitted capability map', + name: 'auditd.data.cap_fp', + type: 'keyword', + }, + 'auditd.data.new-mem': { + category: 'auditd', + description: 'new amount of memory in KB', + name: 'auditd.data.new-mem', + type: 'keyword', + }, + 'auditd.data.seperm': { + category: 'auditd', + description: 'SELinux permission being decided on', + name: 'auditd.data.seperm', + type: 'keyword', + }, + 'auditd.data.enforcing': { + category: 'auditd', + description: 'new MAC enforcement status', + name: 'auditd.data.enforcing', + type: 'keyword', + }, + 'auditd.data.new-chardev': { + category: 'auditd', + description: 'new character device being assigned to vm', + name: 'auditd.data.new-chardev', + type: 'keyword', + }, + 'auditd.data.old-rng': { + category: 'auditd', + description: 'device name of rng being removed from a vm', + name: 'auditd.data.old-rng', + type: 'keyword', + }, + 'auditd.data.outif': { + category: 'auditd', + description: 'out interface number', + name: 'auditd.data.outif', + type: 'keyword', + }, + 'auditd.data.cmd': { + category: 'auditd', + description: 'command being executed', + name: 'auditd.data.cmd', + type: 'keyword', + }, + 'auditd.data.hook': { + category: 'auditd', + description: 'netfilter hook that packet came from', + name: 'auditd.data.hook', + type: 'keyword', + }, + 'auditd.data.new-level': { + category: 'auditd', + description: 'new run level', + name: 'auditd.data.new-level', + type: 'keyword', + }, + 'auditd.data.sauid': { + category: 'auditd', + description: 'sent login user ID', + name: 'auditd.data.sauid', + type: 'keyword', + }, + 'auditd.data.sig': { + category: 'auditd', + description: 'signal number', + name: 'auditd.data.sig', + type: 'keyword', + }, + 'auditd.data.audit_backlog_wait_time': { + category: 'auditd', + description: "audit system's backlog wait time", + name: 'auditd.data.audit_backlog_wait_time', + type: 'keyword', + }, + 'auditd.data.printer': { + category: 'auditd', + description: 'printer name', + name: 'auditd.data.printer', + type: 'keyword', + }, + 'auditd.data.old-mem': { + category: 'auditd', + description: 'present amount of memory in KB', + name: 'auditd.data.old-mem', + type: 'keyword', + }, + 'auditd.data.perm': { + category: 'auditd', + description: 'the file permission being used', + name: 'auditd.data.perm', + type: 'keyword', + }, + 'auditd.data.old_pi': { + category: 'auditd', + description: 'old process inherited capability map', + name: 'auditd.data.old_pi', + type: 'keyword', + }, + 'auditd.data.state': { + category: 'auditd', + description: 'audit daemon configuration resulting state', + name: 'auditd.data.state', + type: 'keyword', + }, + 'auditd.data.format': { + category: 'auditd', + description: "audit log's format", + name: 'auditd.data.format', + type: 'keyword', + }, + 'auditd.data.new_gid': { + category: 'auditd', + description: 'new group ID being assigned', + name: 'auditd.data.new_gid', + type: 'keyword', + }, + 'auditd.data.tcontext': { + category: 'auditd', + description: "the target's or object's context string", + name: 'auditd.data.tcontext', + type: 'keyword', + }, + 'auditd.data.maj': { + category: 'auditd', + description: 'device major number', + name: 'auditd.data.maj', + type: 'keyword', + }, + 'auditd.data.watch': { + category: 'auditd', + description: 'file name in a watch record', + name: 'auditd.data.watch', + type: 'keyword', + }, + 'auditd.data.device': { + category: 'auditd', + description: 'device name', + name: 'auditd.data.device', + type: 'keyword', + }, + 'auditd.data.grp': { + category: 'auditd', + description: 'group name', + name: 'auditd.data.grp', + type: 'keyword', + }, + 'auditd.data.bool': { + category: 'auditd', + description: 'name of SELinux boolean', + name: 'auditd.data.bool', + type: 'keyword', + }, + 'auditd.data.icmp_type': { + category: 'auditd', + description: 'type of icmp message', + name: 'auditd.data.icmp_type', + type: 'keyword', + }, + 'auditd.data.new_lock': { + category: 'auditd', + description: 'new value of feature lock', + name: 'auditd.data.new_lock', + type: 'keyword', + }, + 'auditd.data.old_prom': { + category: 'auditd', + description: 'network promiscuity flag', + name: 'auditd.data.old_prom', + type: 'keyword', + }, + 'auditd.data.acl': { + category: 'auditd', + description: 'access mode of resource assigned to vm', + name: 'auditd.data.acl', + type: 'keyword', + }, + 'auditd.data.ip': { + category: 'auditd', + description: 'network address of a printer', + name: 'auditd.data.ip', + type: 'keyword', + }, + 'auditd.data.new_pi': { + category: 'auditd', + description: 'new process inherited capability map', + name: 'auditd.data.new_pi', + type: 'keyword', + }, + 'auditd.data.default-context': { + category: 'auditd', + description: 'default MAC context', + name: 'auditd.data.default-context', + type: 'keyword', + }, + 'auditd.data.inode_gid': { + category: 'auditd', + description: "group ID of the inode's owner", + name: 'auditd.data.inode_gid', + type: 'keyword', + }, + 'auditd.data.new-log_passwd': { + category: 'auditd', + description: 'new value for TTY password logging', + name: 'auditd.data.new-log_passwd', + type: 'keyword', + }, + 'auditd.data.new_pe': { + category: 'auditd', + description: 'new process effective capability map', + name: 'auditd.data.new_pe', + type: 'keyword', + }, + 'auditd.data.selected-context': { + category: 'auditd', + description: 'new MAC context assigned to session', + name: 'auditd.data.selected-context', + type: 'keyword', + }, + 'auditd.data.cap_fver': { + category: 'auditd', + description: 'file system capabilities version number', + name: 'auditd.data.cap_fver', + type: 'keyword', + }, + 'auditd.data.file': { + category: 'auditd', + description: 'file name', + name: 'auditd.data.file', + type: 'keyword', + }, + 'auditd.data.net': { + category: 'auditd', + description: 'network MAC address', + name: 'auditd.data.net', + type: 'keyword', + }, + 'auditd.data.virt': { + category: 'auditd', + description: 'kind of virtualization being referenced', + name: 'auditd.data.virt', + type: 'keyword', + }, + 'auditd.data.cap_pp': { + category: 'auditd', + description: 'process permitted capability map', + name: 'auditd.data.cap_pp', + type: 'keyword', + }, + 'auditd.data.old-range': { + category: 'auditd', + description: 'present SELinux range', + name: 'auditd.data.old-range', + type: 'keyword', + }, + 'auditd.data.resrc': { + category: 'auditd', + description: 'resource being assigned', + name: 'auditd.data.resrc', + type: 'keyword', + }, + 'auditd.data.new-range': { + category: 'auditd', + description: 'new SELinux range', + name: 'auditd.data.new-range', + type: 'keyword', + }, + 'auditd.data.obj_gid': { + category: 'auditd', + description: 'group ID of object', + name: 'auditd.data.obj_gid', + type: 'keyword', + }, + 'auditd.data.proto': { + category: 'auditd', + description: 'network protocol', + name: 'auditd.data.proto', + type: 'keyword', + }, + 'auditd.data.old-disk': { + category: 'auditd', + description: 'disk being removed from vm', + name: 'auditd.data.old-disk', + type: 'keyword', + }, + 'auditd.data.audit_failure': { + category: 'auditd', + description: "audit system's failure mode", + name: 'auditd.data.audit_failure', + type: 'keyword', + }, + 'auditd.data.inif': { + category: 'auditd', + description: 'in interface number', + name: 'auditd.data.inif', + type: 'keyword', + }, + 'auditd.data.vm': { + category: 'auditd', + description: 'virtual machine name', + name: 'auditd.data.vm', + type: 'keyword', + }, + 'auditd.data.flags': { + category: 'auditd', + description: 'mmap syscall flags', + name: 'auditd.data.flags', + type: 'keyword', + }, + 'auditd.data.nlnk-fam': { + category: 'auditd', + description: 'netlink protocol number', + name: 'auditd.data.nlnk-fam', + type: 'keyword', + }, + 'auditd.data.old-fs': { + category: 'auditd', + description: 'file system being removed from vm', + name: 'auditd.data.old-fs', + type: 'keyword', + }, + 'auditd.data.old-ses': { + category: 'auditd', + description: 'previous ses value', + name: 'auditd.data.old-ses', + type: 'keyword', + }, + 'auditd.data.seqno': { + category: 'auditd', + description: 'sequence number', + name: 'auditd.data.seqno', + type: 'keyword', + }, + 'auditd.data.fver': { + category: 'auditd', + description: 'file system capabilities version number', + name: 'auditd.data.fver', + type: 'keyword', + }, + 'auditd.data.qbytes': { + category: 'auditd', + description: 'ipc objects quantity of bytes', + name: 'auditd.data.qbytes', + type: 'keyword', + }, + 'auditd.data.seuser': { + category: 'auditd', + description: "user's SE Linux user acct", + name: 'auditd.data.seuser', + type: 'keyword', + }, + 'auditd.data.cap_fe': { + category: 'auditd', + description: 'file assigned effective capability map', + name: 'auditd.data.cap_fe', + type: 'keyword', + }, + 'auditd.data.new-vcpu': { + category: 'auditd', + description: 'new number of CPU cores', + name: 'auditd.data.new-vcpu', + type: 'keyword', + }, + 'auditd.data.old-level': { + category: 'auditd', + description: 'old run level', + name: 'auditd.data.old-level', + type: 'keyword', + }, + 'auditd.data.old_pp': { + category: 'auditd', + description: 'old process permitted capability map', + name: 'auditd.data.old_pp', + type: 'keyword', + }, + 'auditd.data.daddr': { + category: 'auditd', + description: 'remote IP address', + name: 'auditd.data.daddr', + type: 'keyword', + }, + 'auditd.data.old-role': { + category: 'auditd', + description: 'present SELinux role', + name: 'auditd.data.old-role', + type: 'keyword', + }, + 'auditd.data.ioctlcmd': { + category: 'auditd', + description: 'The request argument to the ioctl syscall', + name: 'auditd.data.ioctlcmd', + type: 'keyword', + }, + 'auditd.data.smac': { + category: 'auditd', + description: 'local MAC address', + name: 'auditd.data.smac', + type: 'keyword', + }, + 'auditd.data.apparmor': { + category: 'auditd', + description: 'apparmor event information', + name: 'auditd.data.apparmor', + type: 'keyword', + }, + 'auditd.data.fe': { + category: 'auditd', + description: 'file assigned effective capability map', + name: 'auditd.data.fe', + type: 'keyword', + }, + 'auditd.data.perm_mask': { + category: 'auditd', + description: 'file permission mask that triggered a watch event', + name: 'auditd.data.perm_mask', + type: 'keyword', + }, + 'auditd.data.ses': { + category: 'auditd', + description: 'login session ID', + name: 'auditd.data.ses', + type: 'keyword', + }, + 'auditd.data.cap_fi': { + category: 'auditd', + description: 'file inherited capability map', + name: 'auditd.data.cap_fi', + type: 'keyword', + }, + 'auditd.data.obj_uid': { + category: 'auditd', + description: 'user ID of object', + name: 'auditd.data.obj_uid', + type: 'keyword', + }, + 'auditd.data.reason': { + category: 'auditd', + description: 'text string denoting a reason for the action', + name: 'auditd.data.reason', + type: 'keyword', + }, + 'auditd.data.list': { + category: 'auditd', + description: "the audit system's filter list number", + name: 'auditd.data.list', + type: 'keyword', + }, + 'auditd.data.old_lock': { + category: 'auditd', + description: 'present value of feature lock', + name: 'auditd.data.old_lock', + type: 'keyword', + }, + 'auditd.data.bus': { + category: 'auditd', + description: 'name of subsystem bus a vm resource belongs to', + name: 'auditd.data.bus', + type: 'keyword', + }, + 'auditd.data.old_pe': { + category: 'auditd', + description: 'old process effective capability map', + name: 'auditd.data.old_pe', + type: 'keyword', + }, + 'auditd.data.new-role': { + category: 'auditd', + description: 'new SELinux role', + name: 'auditd.data.new-role', + type: 'keyword', + }, + 'auditd.data.prom': { + category: 'auditd', + description: 'network promiscuity flag', + name: 'auditd.data.prom', + type: 'keyword', + }, + 'auditd.data.uri': { + category: 'auditd', + description: 'URI pointing to a printer', + name: 'auditd.data.uri', + type: 'keyword', + }, + 'auditd.data.audit_enabled': { + category: 'auditd', + description: "audit systems's enable/disable status", + name: 'auditd.data.audit_enabled', + type: 'keyword', + }, + 'auditd.data.old-log_passwd': { + category: 'auditd', + description: 'present value for TTY password logging', + name: 'auditd.data.old-log_passwd', + type: 'keyword', + }, + 'auditd.data.old-seuser': { + category: 'auditd', + description: 'present SELinux user', + name: 'auditd.data.old-seuser', + type: 'keyword', + }, + 'auditd.data.per': { + category: 'auditd', + description: 'linux personality', + name: 'auditd.data.per', + type: 'keyword', + }, + 'auditd.data.scontext': { + category: 'auditd', + description: "the subject's context string", + name: 'auditd.data.scontext', + type: 'keyword', + }, + 'auditd.data.tclass': { + category: 'auditd', + description: "target's object classification", + name: 'auditd.data.tclass', + type: 'keyword', + }, + 'auditd.data.ver': { + category: 'auditd', + description: "audit daemon's version number", + name: 'auditd.data.ver', + type: 'keyword', + }, + 'auditd.data.new': { + category: 'auditd', + description: 'value being set in feature', + name: 'auditd.data.new', + type: 'keyword', + }, + 'auditd.data.val': { + category: 'auditd', + description: 'generic value associated with the operation', + name: 'auditd.data.val', + type: 'keyword', + }, + 'auditd.data.img-ctx': { + category: 'auditd', + description: "the vm's disk image context string", + name: 'auditd.data.img-ctx', + type: 'keyword', + }, + 'auditd.data.old-chardev': { + category: 'auditd', + description: 'present character device assigned to vm', + name: 'auditd.data.old-chardev', + type: 'keyword', + }, + 'auditd.data.old_val': { + category: 'auditd', + description: 'current value of SELinux boolean', + name: 'auditd.data.old_val', + type: 'keyword', + }, + 'auditd.data.success': { + category: 'auditd', + description: 'whether the syscall was successful or not', + name: 'auditd.data.success', + type: 'keyword', + }, + 'auditd.data.inode_uid': { + category: 'auditd', + description: "user ID of the inode's owner", + name: 'auditd.data.inode_uid', + type: 'keyword', + }, + 'auditd.data.removed': { + category: 'auditd', + description: 'number of deleted files', + name: 'auditd.data.removed', + type: 'keyword', + }, + 'auditd.data.socket.port': { + category: 'auditd', + description: 'The port number.', + name: 'auditd.data.socket.port', + type: 'keyword', + }, + 'auditd.data.socket.saddr': { + category: 'auditd', + description: 'The raw socket address structure.', + name: 'auditd.data.socket.saddr', + type: 'keyword', + }, + 'auditd.data.socket.addr': { + category: 'auditd', + description: 'The remote address.', + name: 'auditd.data.socket.addr', + type: 'keyword', + }, + 'auditd.data.socket.family': { + category: 'auditd', + description: 'The socket family (unix, ipv4, ipv6, netlink).', + example: 'unix', + name: 'auditd.data.socket.family', + type: 'keyword', + }, + 'auditd.data.socket.path': { + category: 'auditd', + description: 'This is the path associated with a unix socket.', + name: 'auditd.data.socket.path', + type: 'keyword', + }, + 'auditd.messages': { + category: 'auditd', + description: + 'An ordered list of the raw messages received from the kernel that were used to construct this document. This field is present if an error occurred processing the data or if `include_raw_message` is set in the config. ', + name: 'auditd.messages', + type: 'alias', + }, + 'auditd.warnings': { + category: 'auditd', + description: + 'The warnings generated by the Beat during the construction of the event. These are disabled by default and are used for development and debug purposes only. ', + name: 'auditd.warnings', + type: 'alias', + }, + 'geoip.continent_name': { + category: 'geoip', + description: 'The name of the continent. ', + name: 'geoip.continent_name', + type: 'keyword', + }, + 'geoip.city_name': { + category: 'geoip', + description: 'The name of the city. ', + name: 'geoip.city_name', + type: 'keyword', + }, + 'geoip.region_name': { + category: 'geoip', + description: 'The name of the region. ', + name: 'geoip.region_name', + type: 'keyword', + }, + 'geoip.country_iso_code': { + category: 'geoip', + description: 'Country ISO code. ', + name: 'geoip.country_iso_code', + type: 'keyword', + }, + 'geoip.location': { + category: 'geoip', + description: 'The longitude and latitude. ', + name: 'geoip.location', + type: 'geo_point', + }, + 'hash.blake2b_256': { + category: 'hash', + description: 'BLAKE2b-256 hash of the file.', + name: 'hash.blake2b_256', + type: 'keyword', + }, + 'hash.blake2b_384': { + category: 'hash', + description: 'BLAKE2b-384 hash of the file.', + name: 'hash.blake2b_384', + type: 'keyword', + }, + 'hash.blake2b_512': { + category: 'hash', + description: 'BLAKE2b-512 hash of the file.', + name: 'hash.blake2b_512', + type: 'keyword', + }, + 'hash.sha224': { + category: 'hash', + description: 'SHA224 hash of the file.', + name: 'hash.sha224', + type: 'keyword', + }, + 'hash.sha384': { + category: 'hash', + description: 'SHA384 hash of the file.', + name: 'hash.sha384', + type: 'keyword', + }, + 'hash.sha3_224': { + category: 'hash', + description: 'SHA3_224 hash of the file.', + name: 'hash.sha3_224', + type: 'keyword', + }, + 'hash.sha3_256': { + category: 'hash', + description: 'SHA3_256 hash of the file.', + name: 'hash.sha3_256', + type: 'keyword', + }, + 'hash.sha3_384': { + category: 'hash', + description: 'SHA3_384 hash of the file.', + name: 'hash.sha3_384', + type: 'keyword', + }, + 'hash.sha3_512': { + category: 'hash', + description: 'SHA3_512 hash of the file.', + name: 'hash.sha3_512', + type: 'keyword', + }, + 'hash.sha512_224': { + category: 'hash', + description: 'SHA512/224 hash of the file.', + name: 'hash.sha512_224', + type: 'keyword', + }, + 'hash.sha512_256': { + category: 'hash', + description: 'SHA512/256 hash of the file.', + name: 'hash.sha512_256', + type: 'keyword', + }, + 'hash.xxh64': { + category: 'hash', + description: 'XX64 hash of the file.', + name: 'hash.xxh64', + type: 'keyword', + }, + 'event.origin': { + category: 'event', + description: + 'Origin of the event. This can be a file path (e.g. `/var/log/log.1`), or the name of the system component that supplied the data (e.g. `netlink`). ', + name: 'event.origin', + type: 'keyword', + }, + 'user.entity_id': { + category: 'user', + description: + 'ID uniquely identifying the user on a host. It is computed as a SHA-256 hash of the host ID, user ID, and user name. ', + name: 'user.entity_id', + type: 'keyword', + }, + 'user.terminal': { + category: 'user', + description: 'Terminal of the user. ', + name: 'user.terminal', + type: 'keyword', + }, + 'process.hash.blake2b_256': { + category: 'process', + description: 'BLAKE2b-256 hash of the executable.', + name: 'process.hash.blake2b_256', + type: 'keyword', + }, + 'process.hash.blake2b_384': { + category: 'process', + description: 'BLAKE2b-384 hash of the executable.', + name: 'process.hash.blake2b_384', + type: 'keyword', + }, + 'process.hash.blake2b_512': { + category: 'process', + description: 'BLAKE2b-512 hash of the executable.', + name: 'process.hash.blake2b_512', + type: 'keyword', + }, + 'process.hash.sha224': { + category: 'process', + description: 'SHA224 hash of the executable.', + name: 'process.hash.sha224', + type: 'keyword', + }, + 'process.hash.sha384': { + category: 'process', + description: 'SHA384 hash of the executable.', + name: 'process.hash.sha384', + type: 'keyword', + }, + 'process.hash.sha3_224': { + category: 'process', + description: 'SHA3_224 hash of the executable.', + name: 'process.hash.sha3_224', + type: 'keyword', + }, + 'process.hash.sha3_256': { + category: 'process', + description: 'SHA3_256 hash of the executable.', + name: 'process.hash.sha3_256', + type: 'keyword', + }, + 'process.hash.sha3_384': { + category: 'process', + description: 'SHA3_384 hash of the executable.', + name: 'process.hash.sha3_384', + type: 'keyword', + }, + 'process.hash.sha3_512': { + category: 'process', + description: 'SHA3_512 hash of the executable.', + name: 'process.hash.sha3_512', + type: 'keyword', + }, + 'process.hash.sha512_224': { + category: 'process', + description: 'SHA512/224 hash of the executable.', + name: 'process.hash.sha512_224', + type: 'keyword', + }, + 'process.hash.sha512_256': { + category: 'process', + description: 'SHA512/256 hash of the executable.', + name: 'process.hash.sha512_256', + type: 'keyword', + }, + 'process.hash.xxh64': { + category: 'process', + description: 'XX64 hash of the executable.', + name: 'process.hash.xxh64', + type: 'keyword', + }, + 'socket.entity_id': { + category: 'socket', + description: + 'ID uniquely identifying the socket. It is computed as a SHA-256 hash of the host ID, socket inode, local IP, local port, remote IP, and remote port. ', + name: 'socket.entity_id', + type: 'keyword', + }, + 'system.audit.host.uptime': { + category: 'system', + description: 'Uptime in nanoseconds. ', + name: 'system.audit.host.uptime', + type: 'long', + format: 'duration', + }, + 'system.audit.host.boottime': { + category: 'system', + description: 'Boot time. ', + name: 'system.audit.host.boottime', + type: 'date', + }, + 'system.audit.host.containerized': { + category: 'system', + description: 'Set if host is a container. ', + name: 'system.audit.host.containerized', + type: 'boolean', + }, + 'system.audit.host.timezone.name': { + category: 'system', + description: 'Name of the timezone of the host, e.g. BST. ', + name: 'system.audit.host.timezone.name', + type: 'keyword', + }, + 'system.audit.host.timezone.offset.sec': { + category: 'system', + description: 'Timezone offset in seconds. ', + name: 'system.audit.host.timezone.offset.sec', + type: 'long', + }, + 'system.audit.host.hostname': { + category: 'system', + description: 'Hostname. ', + name: 'system.audit.host.hostname', + type: 'keyword', + }, + 'system.audit.host.id': { + category: 'system', + description: 'Host ID. ', + name: 'system.audit.host.id', + type: 'keyword', + }, + 'system.audit.host.architecture': { + category: 'system', + description: 'Host architecture (e.g. x86_64). ', + name: 'system.audit.host.architecture', + type: 'keyword', + }, + 'system.audit.host.mac': { + category: 'system', + description: 'MAC addresses. ', + name: 'system.audit.host.mac', + type: 'keyword', + }, + 'system.audit.host.ip': { + category: 'system', + description: 'IP addresses. ', + name: 'system.audit.host.ip', + type: 'ip', + }, + 'system.audit.host.os.codename': { + category: 'system', + description: 'OS codename, if any (e.g. stretch). ', + name: 'system.audit.host.os.codename', + type: 'keyword', + }, + 'system.audit.host.os.platform': { + category: 'system', + description: 'OS platform (e.g. centos, ubuntu, windows). ', + name: 'system.audit.host.os.platform', + type: 'keyword', + }, + 'system.audit.host.os.name': { + category: 'system', + description: 'OS name (e.g. Mac OS X). ', + name: 'system.audit.host.os.name', + type: 'keyword', + }, + 'system.audit.host.os.family': { + category: 'system', + description: 'OS family (e.g. redhat, debian, freebsd, windows). ', + name: 'system.audit.host.os.family', + type: 'keyword', + }, + 'system.audit.host.os.version': { + category: 'system', + description: 'OS version. ', + name: 'system.audit.host.os.version', + type: 'keyword', + }, + 'system.audit.host.os.kernel': { + category: 'system', + description: "The operating system's kernel version. ", + name: 'system.audit.host.os.kernel', + type: 'keyword', + }, + 'system.audit.package.entity_id': { + category: 'system', + description: + 'ID uniquely identifying the package. It is computed as a SHA-256 hash of the host ID, package name, and package version. ', + name: 'system.audit.package.entity_id', + type: 'keyword', + }, + 'system.audit.package.name': { + category: 'system', + description: 'Package name. ', + name: 'system.audit.package.name', + type: 'keyword', + }, + 'system.audit.package.version': { + category: 'system', + description: 'Package version. ', + name: 'system.audit.package.version', + type: 'keyword', + }, + 'system.audit.package.release': { + category: 'system', + description: 'Package release. ', + name: 'system.audit.package.release', + type: 'keyword', + }, + 'system.audit.package.arch': { + category: 'system', + description: 'Package architecture. ', + name: 'system.audit.package.arch', + type: 'keyword', + }, + 'system.audit.package.license': { + category: 'system', + description: 'Package license. ', + name: 'system.audit.package.license', + type: 'keyword', + }, + 'system.audit.package.installtime': { + category: 'system', + description: 'Package install time. ', + name: 'system.audit.package.installtime', + type: 'date', + }, + 'system.audit.package.size': { + category: 'system', + description: 'Package size. ', + name: 'system.audit.package.size', + type: 'long', + }, + 'system.audit.package.summary': { + category: 'system', + description: 'Package summary. ', + name: 'system.audit.package.summary', + }, + 'system.audit.package.url': { + category: 'system', + description: 'Package URL. ', + name: 'system.audit.package.url', + type: 'keyword', + }, + 'system.audit.user.name': { + category: 'system', + description: 'User name. ', + name: 'system.audit.user.name', + type: 'keyword', + }, + 'system.audit.user.uid': { + category: 'system', + description: 'User ID. ', + name: 'system.audit.user.uid', + type: 'keyword', + }, + 'system.audit.user.gid': { + category: 'system', + description: 'Group ID. ', + name: 'system.audit.user.gid', + type: 'keyword', + }, + 'system.audit.user.dir': { + category: 'system', + description: "User's home directory. ", + name: 'system.audit.user.dir', + type: 'keyword', + }, + 'system.audit.user.shell': { + category: 'system', + description: 'Program to run at login. ', + name: 'system.audit.user.shell', + type: 'keyword', + }, + 'system.audit.user.user_information': { + category: 'system', + description: 'General user information. On Linux, this is the gecos field. ', + name: 'system.audit.user.user_information', + type: 'keyword', + }, + 'system.audit.user.group.name': { + category: 'system', + description: 'Group name. ', + name: 'system.audit.user.group.name', + type: 'keyword', + }, + 'system.audit.user.group.gid': { + category: 'system', + description: 'Group ID. ', + name: 'system.audit.user.group.gid', + type: 'integer', + }, + 'system.audit.user.password.type': { + category: 'system', + description: + "A user's password type. Possible values are `shadow_password` (the password hash is in the shadow file), `password_disabled`, `no_password` (this is dangerous as anyone can log in), and `crypt_password` (when the password field in /etc/passwd seems to contain an encrypted password). ", + name: 'system.audit.user.password.type', + type: 'keyword', + }, + 'system.audit.user.password.last_changed': { + category: 'system', + description: "The day the user's password was last changed. ", + name: 'system.audit.user.password.last_changed', + type: 'date', + }, + 'log.file.path': { + category: 'log', + description: + 'The file from which the line was read. This field contains the absolute path to the file. For example: `/var/log/system.log`. ', + name: 'log.file.path', + type: 'keyword', + }, + 'log.source.address': { + category: 'log', + description: 'Source address from which the log event was read / sent from. ', + name: 'log.source.address', + type: 'keyword', + }, + 'log.offset': { + category: 'log', + description: 'The file offset the reported line starts at. ', + name: 'log.offset', + type: 'long', + }, + stream: { + category: 'base', + description: "Log stream when reading container logs, can be 'stdout' or 'stderr' ", + name: 'stream', + type: 'keyword', + }, + 'input.type': { + category: 'input', + description: + 'The input type from which the event was generated. This field is set to the value specified for the `type` option in the input section of the Filebeat config file. ', + name: 'input.type', + }, + 'syslog.facility': { + category: 'syslog', + description: 'The facility extracted from the priority. ', + name: 'syslog.facility', + type: 'long', + }, + 'syslog.priority': { + category: 'syslog', + description: 'The priority of the syslog event. ', + name: 'syslog.priority', + type: 'long', + }, + 'syslog.severity_label': { + category: 'syslog', + description: 'The human readable severity. ', + name: 'syslog.severity_label', + type: 'keyword', + }, + 'syslog.facility_label': { + category: 'syslog', + description: 'The human readable facility. ', + name: 'syslog.facility_label', + type: 'keyword', + }, + 'process.program': { + category: 'process', + description: 'The name of the program. ', + name: 'process.program', + type: 'keyword', + }, + 'log.flags': { + category: 'log', + description: 'This field contains the flags of the event. ', + name: 'log.flags', + }, + 'http.response.content_length': { + category: 'http', + name: 'http.response.content_length', + type: 'alias', + }, + 'user_agent.os.full_name': { + category: 'user_agent', + name: 'user_agent.os.full_name', + type: 'keyword', + }, + 'fileset.name': { + category: 'fileset', + description: 'The Filebeat fileset that generated this event. ', + name: 'fileset.name', + type: 'keyword', + }, + 'fileset.module': { + category: 'fileset', + name: 'fileset.module', + type: 'alias', + }, + read_timestamp: { + category: 'base', + name: 'read_timestamp', + type: 'alias', + }, + 'docker.attrs': { + category: 'docker', + description: + "docker.attrs contains labels and environment variables written by docker's JSON File logging driver. These fields are only available when they are configured in the logging driver options. ", + name: 'docker.attrs', + type: 'object', + }, + 'icmp.code': { + category: 'icmp', + description: 'ICMP code. ', + name: 'icmp.code', + type: 'keyword', + }, + 'icmp.type': { + category: 'icmp', + description: 'ICMP type. ', + name: 'icmp.type', + type: 'keyword', + }, + 'igmp.type': { + category: 'igmp', + description: 'IGMP type. ', + name: 'igmp.type', + type: 'keyword', + }, + 'azure.eventhub': { + category: 'azure', + description: 'Name of the eventhub. ', + name: 'azure.eventhub', + type: 'keyword', + }, + 'azure.offset': { + category: 'azure', + description: 'The offset. ', + name: 'azure.offset', + type: 'long', + }, + 'azure.enqueued_time': { + category: 'azure', + description: 'The enqueued time. ', + name: 'azure.enqueued_time', + type: 'date', + }, + 'azure.partition_id': { + category: 'azure', + description: 'The partition id. ', + name: 'azure.partition_id', + type: 'long', + }, + 'azure.consumer_group': { + category: 'azure', + description: 'The consumer group. ', + name: 'azure.consumer_group', + type: 'keyword', + }, + 'azure.sequence_number': { + category: 'azure', + description: 'The sequence number. ', + name: 'azure.sequence_number', + type: 'long', + }, + 'kafka.topic': { + category: 'kafka', + description: 'Kafka topic ', + name: 'kafka.topic', + type: 'keyword', + }, + 'kafka.partition': { + category: 'kafka', + description: 'Kafka partition number ', + name: 'kafka.partition', + type: 'long', + }, + 'kafka.offset': { + category: 'kafka', + description: 'Kafka offset of this message ', + name: 'kafka.offset', + type: 'long', + }, + 'kafka.key': { + category: 'kafka', + description: 'Kafka key, corresponding to the Kafka value stored in the message ', + name: 'kafka.key', + type: 'keyword', + }, + 'kafka.block_timestamp': { + category: 'kafka', + description: 'Kafka outer (compressed) block timestamp ', + name: 'kafka.block_timestamp', + type: 'date', + }, + 'kafka.headers': { + category: 'kafka', + description: + 'An array of Kafka header strings for this message, in the form ": ". ', + name: 'kafka.headers', + type: 'array', + }, + 'apache2.access.remote_ip': { + category: 'apache2', + name: 'apache2.access.remote_ip', + type: 'alias', + }, + 'apache2.access.ssl.protocol': { + category: 'apache2', + name: 'apache2.access.ssl.protocol', + type: 'alias', + }, + 'apache2.access.ssl.cipher': { + category: 'apache2', + name: 'apache2.access.ssl.cipher', + type: 'alias', + }, + 'apache2.access.body_sent.bytes': { + category: 'apache2', + name: 'apache2.access.body_sent.bytes', + type: 'alias', + }, + 'apache2.access.user_name': { + category: 'apache2', + name: 'apache2.access.user_name', + type: 'alias', + }, + 'apache2.access.method': { + category: 'apache2', + name: 'apache2.access.method', + type: 'alias', + }, + 'apache2.access.url': { + category: 'apache2', + name: 'apache2.access.url', + type: 'alias', + }, + 'apache2.access.http_version': { + category: 'apache2', + name: 'apache2.access.http_version', + type: 'alias', + }, + 'apache2.access.response_code': { + category: 'apache2', + name: 'apache2.access.response_code', + type: 'alias', + }, + 'apache2.access.referrer': { + category: 'apache2', + name: 'apache2.access.referrer', + type: 'alias', + }, + 'apache2.access.agent': { + category: 'apache2', + name: 'apache2.access.agent', + type: 'alias', + }, + 'apache2.access.user_agent.device': { + category: 'apache2', + name: 'apache2.access.user_agent.device', + type: 'alias', + }, + 'apache2.access.user_agent.name': { + category: 'apache2', + name: 'apache2.access.user_agent.name', + type: 'alias', + }, + 'apache2.access.user_agent.os': { + category: 'apache2', + name: 'apache2.access.user_agent.os', + type: 'alias', + }, + 'apache2.access.user_agent.os_name': { + category: 'apache2', + name: 'apache2.access.user_agent.os_name', + type: 'alias', + }, + 'apache2.access.user_agent.original': { + category: 'apache2', + name: 'apache2.access.user_agent.original', + type: 'alias', + }, + 'apache2.access.geoip.continent_name': { + category: 'apache2', + name: 'apache2.access.geoip.continent_name', + type: 'alias', + }, + 'apache2.access.geoip.country_iso_code': { + category: 'apache2', + name: 'apache2.access.geoip.country_iso_code', + type: 'alias', + }, + 'apache2.access.geoip.location': { + category: 'apache2', + name: 'apache2.access.geoip.location', + type: 'alias', + }, + 'apache2.access.geoip.region_name': { + category: 'apache2', + name: 'apache2.access.geoip.region_name', + type: 'alias', + }, + 'apache2.access.geoip.city_name': { + category: 'apache2', + name: 'apache2.access.geoip.city_name', + type: 'alias', + }, + 'apache2.access.geoip.region_iso_code': { + category: 'apache2', + name: 'apache2.access.geoip.region_iso_code', + type: 'alias', + }, + 'apache2.error.level': { + category: 'apache2', + name: 'apache2.error.level', + type: 'alias', + }, + 'apache2.error.message': { + category: 'apache2', + name: 'apache2.error.message', + type: 'alias', + }, + 'apache2.error.pid': { + category: 'apache2', + name: 'apache2.error.pid', + type: 'alias', + }, + 'apache2.error.tid': { + category: 'apache2', + name: 'apache2.error.tid', + type: 'alias', + }, + 'apache2.error.module': { + category: 'apache2', + name: 'apache2.error.module', + type: 'alias', + }, + 'apache.access.ssl.protocol': { + category: 'apache', + description: 'SSL protocol version. ', + name: 'apache.access.ssl.protocol', + type: 'keyword', + }, + 'apache.access.ssl.cipher': { + category: 'apache', + description: 'SSL cipher name. ', + name: 'apache.access.ssl.cipher', + type: 'keyword', + }, + 'apache.error.module': { + category: 'apache', + description: 'The module producing the logged message. ', + name: 'apache.error.module', + type: 'keyword', + }, + 'user.audit.group.id': { + category: 'user', + description: 'Unique identifier for the group on the system/platform. ', + name: 'user.audit.group.id', + type: 'keyword', + }, + 'user.audit.group.name': { + category: 'user', + description: 'Name of the group. ', + name: 'user.audit.group.name', + type: 'keyword', + }, + 'user.owner.id': { + category: 'user', + description: 'One or multiple unique identifiers of the user. ', + name: 'user.owner.id', + type: 'keyword', + }, + 'user.owner.name': { + category: 'user', + description: 'Short name or login of the user. ', + example: 'albert', + name: 'user.owner.name', + type: 'keyword', + }, + 'user.owner.group.id': { + category: 'user', + description: 'Unique identifier for the group on the system/platform. ', + name: 'user.owner.group.id', + type: 'keyword', + }, + 'user.owner.group.name': { + category: 'user', + description: 'Name of the group. ', + name: 'user.owner.group.name', + type: 'keyword', + }, + 'auditd.log.old_auid': { + category: 'auditd', + description: + 'For login events this is the old audit ID used for the user prior to this login. ', + name: 'auditd.log.old_auid', + }, + 'auditd.log.new_auid': { + category: 'auditd', + description: + 'For login events this is the new audit ID. The audit ID can be used to trace future events to the user even if their identity changes (like becoming root). ', + name: 'auditd.log.new_auid', + }, + 'auditd.log.old_ses': { + category: 'auditd', + description: + 'For login events this is the old session ID used for the user prior to this login. ', + name: 'auditd.log.old_ses', + }, + 'auditd.log.new_ses': { + category: 'auditd', + description: + 'For login events this is the new session ID. It can be used to tie a user to future events by session ID. ', + name: 'auditd.log.new_ses', + }, + 'auditd.log.sequence': { + category: 'auditd', + description: 'The audit event sequence number. ', + name: 'auditd.log.sequence', + type: 'long', + }, + 'auditd.log.items': { + category: 'auditd', + description: 'The number of items in an event. ', + name: 'auditd.log.items', + }, + 'auditd.log.item': { + category: 'auditd', + description: + 'The item field indicates which item out of the total number of items. This number is zero-based; a value of 0 means it is the first item. ', + name: 'auditd.log.item', + }, + 'auditd.log.tty': { + category: 'auditd', + name: 'auditd.log.tty', + type: 'keyword', + }, + 'auditd.log.a0': { + category: 'auditd', + description: 'The first argument to the system call. ', + name: 'auditd.log.a0', + }, + 'auditd.log.addr': { + category: 'auditd', + name: 'auditd.log.addr', + type: 'ip', + }, + 'auditd.log.rport': { + category: 'auditd', + name: 'auditd.log.rport', + type: 'long', + }, + 'auditd.log.laddr': { + category: 'auditd', + name: 'auditd.log.laddr', + type: 'ip', + }, + 'auditd.log.lport': { + category: 'auditd', + name: 'auditd.log.lport', + type: 'long', + }, + 'auditd.log.acct': { + category: 'auditd', + name: 'auditd.log.acct', + type: 'alias', + }, + 'auditd.log.pid': { + category: 'auditd', + name: 'auditd.log.pid', + type: 'alias', + }, + 'auditd.log.ppid': { + category: 'auditd', + name: 'auditd.log.ppid', + type: 'alias', + }, + 'auditd.log.res': { + category: 'auditd', + name: 'auditd.log.res', + type: 'alias', + }, + 'auditd.log.record_type': { + category: 'auditd', + name: 'auditd.log.record_type', + type: 'alias', + }, + 'auditd.log.geoip.continent_name': { + category: 'auditd', + name: 'auditd.log.geoip.continent_name', + type: 'alias', + }, + 'auditd.log.geoip.country_iso_code': { + category: 'auditd', + name: 'auditd.log.geoip.country_iso_code', + type: 'alias', + }, + 'auditd.log.geoip.location': { + category: 'auditd', + name: 'auditd.log.geoip.location', + type: 'alias', + }, + 'auditd.log.geoip.region_name': { + category: 'auditd', + name: 'auditd.log.geoip.region_name', + type: 'alias', + }, + 'auditd.log.geoip.city_name': { + category: 'auditd', + name: 'auditd.log.geoip.city_name', + type: 'alias', + }, + 'auditd.log.geoip.region_iso_code': { + category: 'auditd', + name: 'auditd.log.geoip.region_iso_code', + type: 'alias', + }, + 'auditd.log.arch': { + category: 'auditd', + name: 'auditd.log.arch', + type: 'alias', + }, + 'auditd.log.gid': { + category: 'auditd', + name: 'auditd.log.gid', + type: 'alias', + }, + 'auditd.log.uid': { + category: 'auditd', + name: 'auditd.log.uid', + type: 'alias', + }, + 'auditd.log.agid': { + category: 'auditd', + name: 'auditd.log.agid', + type: 'alias', + }, + 'auditd.log.auid': { + category: 'auditd', + name: 'auditd.log.auid', + type: 'alias', + }, + 'auditd.log.fsgid': { + category: 'auditd', + name: 'auditd.log.fsgid', + type: 'alias', + }, + 'auditd.log.fsuid': { + category: 'auditd', + name: 'auditd.log.fsuid', + type: 'alias', + }, + 'auditd.log.egid': { + category: 'auditd', + name: 'auditd.log.egid', + type: 'alias', + }, + 'auditd.log.euid': { + category: 'auditd', + name: 'auditd.log.euid', + type: 'alias', + }, + 'auditd.log.sgid': { + category: 'auditd', + name: 'auditd.log.sgid', + type: 'alias', + }, + 'auditd.log.suid': { + category: 'auditd', + name: 'auditd.log.suid', + type: 'alias', + }, + 'auditd.log.ogid': { + category: 'auditd', + name: 'auditd.log.ogid', + type: 'alias', + }, + 'auditd.log.ouid': { + category: 'auditd', + name: 'auditd.log.ouid', + type: 'alias', + }, + 'auditd.log.comm': { + category: 'auditd', + name: 'auditd.log.comm', + type: 'alias', + }, + 'auditd.log.exe': { + category: 'auditd', + name: 'auditd.log.exe', + type: 'alias', + }, + 'auditd.log.terminal': { + category: 'auditd', + name: 'auditd.log.terminal', + type: 'alias', + }, + 'auditd.log.msg': { + category: 'auditd', + name: 'auditd.log.msg', + type: 'alias', + }, + 'auditd.log.src': { + category: 'auditd', + name: 'auditd.log.src', + type: 'alias', + }, + 'auditd.log.dst': { + category: 'auditd', + name: 'auditd.log.dst', + type: 'alias', + }, + 'elasticsearch.component': { + category: 'elasticsearch', + description: 'Elasticsearch component from where the log event originated', + example: 'o.e.c.m.MetaDataCreateIndexService', + name: 'elasticsearch.component', + type: 'keyword', + }, + 'elasticsearch.cluster.uuid': { + category: 'elasticsearch', + description: 'UUID of the cluster', + example: 'GmvrbHlNTiSVYiPf8kxg9g', + name: 'elasticsearch.cluster.uuid', + type: 'keyword', + }, + 'elasticsearch.cluster.name': { + category: 'elasticsearch', + description: 'Name of the cluster', + example: 'docker-cluster', + name: 'elasticsearch.cluster.name', + type: 'keyword', + }, + 'elasticsearch.node.id': { + category: 'elasticsearch', + description: 'ID of the node', + example: 'DSiWcTyeThWtUXLB9J0BMw', + name: 'elasticsearch.node.id', + type: 'keyword', + }, + 'elasticsearch.node.name': { + category: 'elasticsearch', + description: 'Name of the node', + example: 'vWNJsZ3', + name: 'elasticsearch.node.name', + type: 'keyword', + }, + 'elasticsearch.index.name': { + category: 'elasticsearch', + description: 'Index name', + example: 'filebeat-test-input', + name: 'elasticsearch.index.name', + type: 'keyword', + }, + 'elasticsearch.index.id': { + category: 'elasticsearch', + description: 'Index id', + example: 'aOGgDwbURfCV57AScqbCgw', + name: 'elasticsearch.index.id', + type: 'keyword', + }, + 'elasticsearch.shard.id': { + category: 'elasticsearch', + description: 'Id of the shard', + example: '0', + name: 'elasticsearch.shard.id', + type: 'keyword', + }, + 'elasticsearch.audit.layer': { + category: 'elasticsearch', + description: 'The layer from which this event originated: rest, transport or ip_filter', + example: 'rest', + name: 'elasticsearch.audit.layer', + type: 'keyword', + }, + 'elasticsearch.audit.event_type': { + category: 'elasticsearch', + description: + 'The type of event that occurred: anonymous_access_denied, authentication_failed, access_denied, access_granted, connection_granted, connection_denied, tampered_request, run_as_granted, run_as_denied', + example: 'access_granted', + name: 'elasticsearch.audit.event_type', + type: 'keyword', + }, + 'elasticsearch.audit.origin.type': { + category: 'elasticsearch', + description: + 'Where the request originated: rest (request originated from a REST API request), transport (request was received on the transport channel), local_node (the local node issued the request)', + example: 'local_node', + name: 'elasticsearch.audit.origin.type', + type: 'keyword', + }, + 'elasticsearch.audit.realm': { + category: 'elasticsearch', + description: 'The authentication realm the authentication was validated against', + name: 'elasticsearch.audit.realm', + type: 'keyword', + }, + 'elasticsearch.audit.user.realm': { + category: 'elasticsearch', + description: "The user's authentication realm, if authenticated", + name: 'elasticsearch.audit.user.realm', + type: 'keyword', + }, + 'elasticsearch.audit.user.roles': { + category: 'elasticsearch', + description: 'Roles to which the principal belongs', + example: '["kibana_admin","beats_admin"]', + name: 'elasticsearch.audit.user.roles', + type: 'keyword', + }, + 'elasticsearch.audit.action': { + category: 'elasticsearch', + description: 'The name of the action that was executed', + example: 'cluster:monitor/main', + name: 'elasticsearch.audit.action', + type: 'keyword', + }, + 'elasticsearch.audit.url.params': { + category: 'elasticsearch', + description: 'REST URI parameters', + example: '{username=jacknich2}', + name: 'elasticsearch.audit.url.params', + }, + 'elasticsearch.audit.indices': { + category: 'elasticsearch', + description: 'Indices accessed by action', + example: '["foo-2019.01.04","foo-2019.01.03","foo-2019.01.06"]', + name: 'elasticsearch.audit.indices', + type: 'keyword', + }, + 'elasticsearch.audit.request.id': { + category: 'elasticsearch', + description: 'Unique ID of request', + example: 'WzL_kb6VSvOhAq0twPvHOQ', + name: 'elasticsearch.audit.request.id', + type: 'keyword', + }, + 'elasticsearch.audit.request.name': { + category: 'elasticsearch', + description: 'The type of request that was executed', + example: 'ClearScrollRequest', + name: 'elasticsearch.audit.request.name', + type: 'keyword', + }, + 'elasticsearch.audit.request_body': { + category: 'elasticsearch', + name: 'elasticsearch.audit.request_body', + type: 'alias', + }, + 'elasticsearch.audit.origin_address': { + category: 'elasticsearch', + name: 'elasticsearch.audit.origin_address', + type: 'alias', + }, + 'elasticsearch.audit.uri': { + category: 'elasticsearch', + name: 'elasticsearch.audit.uri', + type: 'alias', + }, + 'elasticsearch.audit.principal': { + category: 'elasticsearch', + name: 'elasticsearch.audit.principal', + type: 'alias', + }, + 'elasticsearch.audit.message': { + category: 'elasticsearch', + name: 'elasticsearch.audit.message', + type: 'text', + }, + 'elasticsearch.deprecation': { + category: 'elasticsearch', + description: '', + name: 'elasticsearch.deprecation', + type: 'group', + }, + 'elasticsearch.gc.phase.name': { + category: 'elasticsearch', + description: 'Name of the GC collection phase. ', + name: 'elasticsearch.gc.phase.name', + type: 'keyword', + }, + 'elasticsearch.gc.phase.duration_sec': { + category: 'elasticsearch', + description: 'Collection phase duration according to the Java virtual machine. ', + name: 'elasticsearch.gc.phase.duration_sec', + type: 'float', + }, + 'elasticsearch.gc.phase.scrub_symbol_table_time_sec': { + category: 'elasticsearch', + description: 'Pause time in seconds cleaning up symbol tables. ', + name: 'elasticsearch.gc.phase.scrub_symbol_table_time_sec', + type: 'float', + }, + 'elasticsearch.gc.phase.scrub_string_table_time_sec': { + category: 'elasticsearch', + description: 'Pause time in seconds cleaning up string tables. ', + name: 'elasticsearch.gc.phase.scrub_string_table_time_sec', + type: 'float', + }, + 'elasticsearch.gc.phase.weak_refs_processing_time_sec': { + category: 'elasticsearch', + description: 'Time spent processing weak references in seconds. ', + name: 'elasticsearch.gc.phase.weak_refs_processing_time_sec', + type: 'float', + }, + 'elasticsearch.gc.phase.parallel_rescan_time_sec': { + category: 'elasticsearch', + description: 'Time spent in seconds marking live objects while application is stopped. ', + name: 'elasticsearch.gc.phase.parallel_rescan_time_sec', + type: 'float', + }, + 'elasticsearch.gc.phase.class_unload_time_sec': { + category: 'elasticsearch', + description: 'Time spent unloading unused classes in seconds. ', + name: 'elasticsearch.gc.phase.class_unload_time_sec', + type: 'float', + }, + 'elasticsearch.gc.phase.cpu_time.user_sec': { + category: 'elasticsearch', + description: 'CPU time spent outside the kernel. ', + name: 'elasticsearch.gc.phase.cpu_time.user_sec', + type: 'float', + }, + 'elasticsearch.gc.phase.cpu_time.sys_sec': { + category: 'elasticsearch', + description: 'CPU time spent inside the kernel. ', + name: 'elasticsearch.gc.phase.cpu_time.sys_sec', + type: 'float', + }, + 'elasticsearch.gc.phase.cpu_time.real_sec': { + category: 'elasticsearch', + description: 'Total elapsed CPU time spent to complete the collection from start to finish. ', + name: 'elasticsearch.gc.phase.cpu_time.real_sec', + type: 'float', + }, + 'elasticsearch.gc.jvm_runtime_sec': { + category: 'elasticsearch', + description: 'The time from JVM start up in seconds, as a floating point number. ', + name: 'elasticsearch.gc.jvm_runtime_sec', + type: 'float', + }, + 'elasticsearch.gc.threads_total_stop_time_sec': { + category: 'elasticsearch', + description: 'Garbage collection threads total stop time seconds. ', + name: 'elasticsearch.gc.threads_total_stop_time_sec', + type: 'float', + }, + 'elasticsearch.gc.stopping_threads_time_sec': { + category: 'elasticsearch', + description: 'Time took to stop threads seconds. ', + name: 'elasticsearch.gc.stopping_threads_time_sec', + type: 'float', + }, + 'elasticsearch.gc.tags': { + category: 'elasticsearch', + description: 'GC logging tags. ', + name: 'elasticsearch.gc.tags', + type: 'keyword', + }, + 'elasticsearch.gc.heap.size_kb': { + category: 'elasticsearch', + description: 'Total heap size in kilobytes. ', + name: 'elasticsearch.gc.heap.size_kb', + type: 'integer', + }, + 'elasticsearch.gc.heap.used_kb': { + category: 'elasticsearch', + description: 'Used heap in kilobytes. ', + name: 'elasticsearch.gc.heap.used_kb', + type: 'integer', + }, + 'elasticsearch.gc.old_gen.size_kb': { + category: 'elasticsearch', + description: 'Total size of old generation in kilobytes. ', + name: 'elasticsearch.gc.old_gen.size_kb', + type: 'integer', + }, + 'elasticsearch.gc.old_gen.used_kb': { + category: 'elasticsearch', + description: 'Old generation occupancy in kilobytes. ', + name: 'elasticsearch.gc.old_gen.used_kb', + type: 'integer', + }, + 'elasticsearch.gc.young_gen.size_kb': { + category: 'elasticsearch', + description: 'Total size of young generation in kilobytes. ', + name: 'elasticsearch.gc.young_gen.size_kb', + type: 'integer', + }, + 'elasticsearch.gc.young_gen.used_kb': { + category: 'elasticsearch', + description: 'Young generation occupancy in kilobytes. ', + name: 'elasticsearch.gc.young_gen.used_kb', + type: 'integer', + }, + 'elasticsearch.server.stacktrace': { + category: 'elasticsearch', + name: 'elasticsearch.server.stacktrace', + }, + 'elasticsearch.server.gc.young.one': { + category: 'elasticsearch', + description: '', + example: '', + name: 'elasticsearch.server.gc.young.one', + type: 'long', + }, + 'elasticsearch.server.gc.young.two': { + category: 'elasticsearch', + description: '', + example: '', + name: 'elasticsearch.server.gc.young.two', + type: 'long', + }, + 'elasticsearch.server.gc.overhead_seq': { + category: 'elasticsearch', + description: 'Sequence number', + example: 3449992, + name: 'elasticsearch.server.gc.overhead_seq', + type: 'long', + }, + 'elasticsearch.server.gc.collection_duration.ms': { + category: 'elasticsearch', + description: 'Time spent in GC, in milliseconds', + example: 1600, + name: 'elasticsearch.server.gc.collection_duration.ms', + type: 'float', + }, + 'elasticsearch.server.gc.observation_duration.ms': { + category: 'elasticsearch', + description: 'Total time over which collection was observed, in milliseconds', + example: 1800, + name: 'elasticsearch.server.gc.observation_duration.ms', + type: 'float', + }, + 'elasticsearch.slowlog.logger': { + category: 'elasticsearch', + description: 'Logger name', + example: 'index.search.slowlog.fetch', + name: 'elasticsearch.slowlog.logger', + type: 'keyword', + }, + 'elasticsearch.slowlog.took': { + category: 'elasticsearch', + description: 'Time it took to execute the query', + example: '300ms', + name: 'elasticsearch.slowlog.took', + type: 'keyword', + }, + 'elasticsearch.slowlog.types': { + category: 'elasticsearch', + description: 'Types', + example: '', + name: 'elasticsearch.slowlog.types', + type: 'keyword', + }, + 'elasticsearch.slowlog.stats': { + category: 'elasticsearch', + description: 'Stats groups', + example: 'group1', + name: 'elasticsearch.slowlog.stats', + type: 'keyword', + }, + 'elasticsearch.slowlog.search_type': { + category: 'elasticsearch', + description: 'Search type', + example: 'QUERY_THEN_FETCH', + name: 'elasticsearch.slowlog.search_type', + type: 'keyword', + }, + 'elasticsearch.slowlog.source_query': { + category: 'elasticsearch', + description: 'Slow query', + example: '{"query":{"match_all":{"boost":1.0}}}', + name: 'elasticsearch.slowlog.source_query', + type: 'keyword', + }, + 'elasticsearch.slowlog.extra_source': { + category: 'elasticsearch', + description: 'Extra source information', + example: '', + name: 'elasticsearch.slowlog.extra_source', + type: 'keyword', + }, + 'elasticsearch.slowlog.total_hits': { + category: 'elasticsearch', + description: 'Total hits', + example: 42, + name: 'elasticsearch.slowlog.total_hits', + type: 'keyword', + }, + 'elasticsearch.slowlog.total_shards': { + category: 'elasticsearch', + description: 'Total queried shards', + example: 22, + name: 'elasticsearch.slowlog.total_shards', + type: 'keyword', + }, + 'elasticsearch.slowlog.routing': { + category: 'elasticsearch', + description: 'Routing', + example: 's01HZ2QBk9jw4gtgaFtn', + name: 'elasticsearch.slowlog.routing', + type: 'keyword', + }, + 'elasticsearch.slowlog.id': { + category: 'elasticsearch', + description: 'Id', + example: '', + name: 'elasticsearch.slowlog.id', + type: 'keyword', + }, + 'elasticsearch.slowlog.type': { + category: 'elasticsearch', + description: 'Type', + example: 'doc', + name: 'elasticsearch.slowlog.type', + type: 'keyword', + }, + 'elasticsearch.slowlog.source': { + category: 'elasticsearch', + description: 'Source of document that was indexed', + name: 'elasticsearch.slowlog.source', + type: 'keyword', + }, + 'haproxy.frontend_name': { + category: 'haproxy', + description: 'Name of the frontend (or listener) which received and processed the connection.', + name: 'haproxy.frontend_name', + }, + 'haproxy.backend_name': { + category: 'haproxy', + description: + 'Name of the backend (or listener) which was selected to manage the connection to the server.', + name: 'haproxy.backend_name', + }, + 'haproxy.server_name': { + category: 'haproxy', + description: 'Name of the last server to which the connection was sent.', + name: 'haproxy.server_name', + }, + 'haproxy.total_waiting_time_ms': { + category: 'haproxy', + description: 'Total time in milliseconds spent waiting in the various queues', + name: 'haproxy.total_waiting_time_ms', + type: 'long', + }, + 'haproxy.connection_wait_time_ms': { + category: 'haproxy', + description: + 'Total time in milliseconds spent waiting for the connection to establish to the final server', + name: 'haproxy.connection_wait_time_ms', + type: 'long', + }, + 'haproxy.bytes_read': { + category: 'haproxy', + description: 'Total number of bytes transmitted to the client when the log is emitted.', + name: 'haproxy.bytes_read', + type: 'long', + }, + 'haproxy.time_queue': { + category: 'haproxy', + description: 'Total time in milliseconds spent waiting in the various queues.', + name: 'haproxy.time_queue', + type: 'long', + }, + 'haproxy.time_backend_connect': { + category: 'haproxy', + description: + 'Total time in milliseconds spent waiting for the connection to establish to the final server, including retries.', + name: 'haproxy.time_backend_connect', + type: 'long', + }, + 'haproxy.server_queue': { + category: 'haproxy', + description: + 'Total number of requests which were processed before this one in the server queue.', + name: 'haproxy.server_queue', + type: 'long', + }, + 'haproxy.backend_queue': { + category: 'haproxy', + description: + "Total number of requests which were processed before this one in the backend's global queue.", + name: 'haproxy.backend_queue', + type: 'long', + }, + 'haproxy.bind_name': { + category: 'haproxy', + description: 'Name of the listening address which received the connection.', + name: 'haproxy.bind_name', + }, + 'haproxy.error_message': { + category: 'haproxy', + description: 'Error message logged by HAProxy in case of error.', + name: 'haproxy.error_message', + type: 'text', + }, + 'haproxy.source': { + category: 'haproxy', + description: 'The HAProxy source of the log', + name: 'haproxy.source', + type: 'keyword', + }, + 'haproxy.termination_state': { + category: 'haproxy', + description: 'Condition the session was in when the session ended.', + name: 'haproxy.termination_state', + }, + 'haproxy.mode': { + category: 'haproxy', + description: 'mode that the frontend is operating (TCP or HTTP)', + name: 'haproxy.mode', + type: 'keyword', + }, + 'haproxy.connections.active': { + category: 'haproxy', + description: + 'Total number of concurrent connections on the process when the session was logged.', + name: 'haproxy.connections.active', + type: 'long', + }, + 'haproxy.connections.frontend': { + category: 'haproxy', + description: + 'Total number of concurrent connections on the frontend when the session was logged.', + name: 'haproxy.connections.frontend', + type: 'long', + }, + 'haproxy.connections.backend': { + category: 'haproxy', + description: + 'Total number of concurrent connections handled by the backend when the session was logged.', + name: 'haproxy.connections.backend', + type: 'long', + }, + 'haproxy.connections.server': { + category: 'haproxy', + description: + 'Total number of concurrent connections still active on the server when the session was logged.', + name: 'haproxy.connections.server', + type: 'long', + }, + 'haproxy.connections.retries': { + category: 'haproxy', + description: + 'Number of connection retries experienced by this session when trying to connect to the server.', + name: 'haproxy.connections.retries', + type: 'long', + }, + 'haproxy.client.ip': { + category: 'haproxy', + name: 'haproxy.client.ip', + type: 'alias', + }, + 'haproxy.client.port': { + category: 'haproxy', + name: 'haproxy.client.port', + type: 'alias', + }, + 'haproxy.process_name': { + category: 'haproxy', + name: 'haproxy.process_name', + type: 'alias', + }, + 'haproxy.pid': { + category: 'haproxy', + name: 'haproxy.pid', + type: 'alias', + }, + 'haproxy.destination.port': { + category: 'haproxy', + name: 'haproxy.destination.port', + type: 'alias', + }, + 'haproxy.destination.ip': { + category: 'haproxy', + name: 'haproxy.destination.ip', + type: 'alias', + }, + 'haproxy.geoip.continent_name': { + category: 'haproxy', + name: 'haproxy.geoip.continent_name', + type: 'alias', + }, + 'haproxy.geoip.country_iso_code': { + category: 'haproxy', + name: 'haproxy.geoip.country_iso_code', + type: 'alias', + }, + 'haproxy.geoip.location': { + category: 'haproxy', + name: 'haproxy.geoip.location', + type: 'alias', + }, + 'haproxy.geoip.region_name': { + category: 'haproxy', + name: 'haproxy.geoip.region_name', + type: 'alias', + }, + 'haproxy.geoip.city_name': { + category: 'haproxy', + name: 'haproxy.geoip.city_name', + type: 'alias', + }, + 'haproxy.geoip.region_iso_code': { + category: 'haproxy', + name: 'haproxy.geoip.region_iso_code', + type: 'alias', + }, + 'haproxy.http.response.captured_cookie': { + category: 'haproxy', + description: + 'Optional "name=value" entry indicating that the client had this cookie in the response. ', + name: 'haproxy.http.response.captured_cookie', + }, + 'haproxy.http.response.captured_headers': { + category: 'haproxy', + description: + 'List of headers captured in the response due to the presence of the "capture response header" statement in the frontend. ', + name: 'haproxy.http.response.captured_headers', + type: 'keyword', + }, + 'haproxy.http.response.status_code': { + category: 'haproxy', + name: 'haproxy.http.response.status_code', + type: 'alias', + }, + 'haproxy.http.request.captured_cookie': { + category: 'haproxy', + description: + 'Optional "name=value" entry indicating that the server has returned a cookie with its request. ', + name: 'haproxy.http.request.captured_cookie', + }, + 'haproxy.http.request.captured_headers': { + category: 'haproxy', + description: + 'List of headers captured in the request due to the presence of the "capture request header" statement in the frontend. ', + name: 'haproxy.http.request.captured_headers', + type: 'keyword', + }, + 'haproxy.http.request.raw_request_line': { + category: 'haproxy', + description: + 'Complete HTTP request line, including the method, request and HTTP version string.', + name: 'haproxy.http.request.raw_request_line', + type: 'keyword', + }, + 'haproxy.http.request.time_wait_without_data_ms': { + category: 'haproxy', + description: + 'Total time in milliseconds spent waiting for the server to send a full HTTP response, not counting data.', + name: 'haproxy.http.request.time_wait_without_data_ms', + type: 'long', + }, + 'haproxy.http.request.time_wait_ms': { + category: 'haproxy', + description: + 'Total time in milliseconds spent waiting for a full HTTP request from the client (not counting body) after the first byte was received.', + name: 'haproxy.http.request.time_wait_ms', + type: 'long', + }, + 'haproxy.tcp.connection_waiting_time_ms': { + category: 'haproxy', + description: 'Total time in milliseconds elapsed between the accept and the last close', + name: 'haproxy.tcp.connection_waiting_time_ms', + type: 'long', + }, + 'icinga.debug.facility': { + category: 'icinga', + description: 'Specifies what component of Icinga logged the message. ', + name: 'icinga.debug.facility', + type: 'keyword', + }, + 'icinga.debug.severity': { + category: 'icinga', + name: 'icinga.debug.severity', + type: 'alias', + }, + 'icinga.debug.message': { + category: 'icinga', + name: 'icinga.debug.message', + type: 'alias', + }, + 'icinga.main.facility': { + category: 'icinga', + description: 'Specifies what component of Icinga logged the message. ', + name: 'icinga.main.facility', + type: 'keyword', + }, + 'icinga.main.severity': { + category: 'icinga', + name: 'icinga.main.severity', + type: 'alias', + }, + 'icinga.main.message': { + category: 'icinga', + name: 'icinga.main.message', + type: 'alias', + }, + 'icinga.startup.facility': { + category: 'icinga', + description: 'Specifies what component of Icinga logged the message. ', + name: 'icinga.startup.facility', + type: 'keyword', + }, + 'icinga.startup.severity': { + category: 'icinga', + name: 'icinga.startup.severity', + type: 'alias', + }, + 'icinga.startup.message': { + category: 'icinga', + name: 'icinga.startup.message', + type: 'alias', + }, + 'iis.access.sub_status': { + category: 'iis', + description: 'The HTTP substatus code. ', + name: 'iis.access.sub_status', + type: 'long', + }, + 'iis.access.win32_status': { + category: 'iis', + description: 'The Windows status code. ', + name: 'iis.access.win32_status', + type: 'long', + }, + 'iis.access.site_name': { + category: 'iis', + description: 'The site name and instance number. ', + name: 'iis.access.site_name', + type: 'keyword', + }, + 'iis.access.server_name': { + category: 'iis', + description: 'The name of the server on which the log file entry was generated. ', + name: 'iis.access.server_name', + type: 'keyword', + }, + 'iis.access.cookie': { + category: 'iis', + description: 'The content of the cookie sent or received, if any. ', + name: 'iis.access.cookie', + type: 'keyword', + }, + 'iis.access.body_received.bytes': { + category: 'iis', + name: 'iis.access.body_received.bytes', + type: 'alias', + }, + 'iis.access.body_sent.bytes': { + category: 'iis', + name: 'iis.access.body_sent.bytes', + type: 'alias', + }, + 'iis.access.server_ip': { + category: 'iis', + name: 'iis.access.server_ip', + type: 'alias', + }, + 'iis.access.method': { + category: 'iis', + name: 'iis.access.method', + type: 'alias', + }, + 'iis.access.url': { + category: 'iis', + name: 'iis.access.url', + type: 'alias', + }, + 'iis.access.query_string': { + category: 'iis', + name: 'iis.access.query_string', + type: 'alias', + }, + 'iis.access.port': { + category: 'iis', + name: 'iis.access.port', + type: 'alias', + }, + 'iis.access.user_name': { + category: 'iis', + name: 'iis.access.user_name', + type: 'alias', + }, + 'iis.access.remote_ip': { + category: 'iis', + name: 'iis.access.remote_ip', + type: 'alias', + }, + 'iis.access.referrer': { + category: 'iis', + name: 'iis.access.referrer', + type: 'alias', + }, + 'iis.access.response_code': { + category: 'iis', + name: 'iis.access.response_code', + type: 'alias', + }, + 'iis.access.http_version': { + category: 'iis', + name: 'iis.access.http_version', + type: 'alias', + }, + 'iis.access.hostname': { + category: 'iis', + name: 'iis.access.hostname', + type: 'alias', + }, + 'iis.access.user_agent.device': { + category: 'iis', + name: 'iis.access.user_agent.device', + type: 'alias', + }, + 'iis.access.user_agent.name': { + category: 'iis', + name: 'iis.access.user_agent.name', + type: 'alias', + }, + 'iis.access.user_agent.os': { + category: 'iis', + name: 'iis.access.user_agent.os', + type: 'alias', + }, + 'iis.access.user_agent.os_name': { + category: 'iis', + name: 'iis.access.user_agent.os_name', + type: 'alias', + }, + 'iis.access.user_agent.original': { + category: 'iis', + name: 'iis.access.user_agent.original', + type: 'alias', + }, + 'iis.access.geoip.continent_name': { + category: 'iis', + name: 'iis.access.geoip.continent_name', + type: 'alias', + }, + 'iis.access.geoip.country_iso_code': { + category: 'iis', + name: 'iis.access.geoip.country_iso_code', + type: 'alias', + }, + 'iis.access.geoip.location': { + category: 'iis', + name: 'iis.access.geoip.location', + type: 'alias', + }, + 'iis.access.geoip.region_name': { + category: 'iis', + name: 'iis.access.geoip.region_name', + type: 'alias', + }, + 'iis.access.geoip.city_name': { + category: 'iis', + name: 'iis.access.geoip.city_name', + type: 'alias', + }, + 'iis.access.geoip.region_iso_code': { + category: 'iis', + name: 'iis.access.geoip.region_iso_code', + type: 'alias', + }, + 'iis.error.reason_phrase': { + category: 'iis', + description: 'The HTTP reason phrase. ', + name: 'iis.error.reason_phrase', + type: 'keyword', + }, + 'iis.error.queue_name': { + category: 'iis', + description: 'The IIS application pool name. ', + name: 'iis.error.queue_name', + type: 'keyword', + }, + 'iis.error.remote_ip': { + category: 'iis', + name: 'iis.error.remote_ip', + type: 'alias', + }, + 'iis.error.remote_port': { + category: 'iis', + name: 'iis.error.remote_port', + type: 'alias', + }, + 'iis.error.server_ip': { + category: 'iis', + name: 'iis.error.server_ip', + type: 'alias', + }, + 'iis.error.server_port': { + category: 'iis', + name: 'iis.error.server_port', + type: 'alias', + }, + 'iis.error.http_version': { + category: 'iis', + name: 'iis.error.http_version', + type: 'alias', + }, + 'iis.error.method': { + category: 'iis', + name: 'iis.error.method', + type: 'alias', + }, + 'iis.error.url': { + category: 'iis', + name: 'iis.error.url', + type: 'alias', + }, + 'iis.error.response_code': { + category: 'iis', + name: 'iis.error.response_code', + type: 'alias', + }, + 'iis.error.geoip.continent_name': { + category: 'iis', + name: 'iis.error.geoip.continent_name', + type: 'alias', + }, + 'iis.error.geoip.country_iso_code': { + category: 'iis', + name: 'iis.error.geoip.country_iso_code', + type: 'alias', + }, + 'iis.error.geoip.location': { + category: 'iis', + name: 'iis.error.geoip.location', + type: 'alias', + }, + 'iis.error.geoip.region_name': { + category: 'iis', + name: 'iis.error.geoip.region_name', + type: 'alias', + }, + 'iis.error.geoip.city_name': { + category: 'iis', + name: 'iis.error.geoip.city_name', + type: 'alias', + }, + 'iis.error.geoip.region_iso_code': { + category: 'iis', + name: 'iis.error.geoip.region_iso_code', + type: 'alias', + }, + 'kafka.log.level': { + category: 'kafka', + name: 'kafka.log.level', + type: 'alias', + }, + 'kafka.log.message': { + category: 'kafka', + name: 'kafka.log.message', + type: 'alias', + }, + 'kafka.log.component': { + category: 'kafka', + description: 'Component the log is coming from. ', + name: 'kafka.log.component', + type: 'keyword', + }, + 'kafka.log.class': { + category: 'kafka', + description: 'Java class the log is coming from. ', + name: 'kafka.log.class', + type: 'keyword', + }, + 'kafka.log.thread': { + category: 'kafka', + description: 'Thread name the log is coming from. ', + name: 'kafka.log.thread', + type: 'keyword', + }, + 'kafka.log.trace.class': { + category: 'kafka', + description: 'Java class the trace is coming from. ', + name: 'kafka.log.trace.class', + type: 'keyword', + }, + 'kafka.log.trace.message': { + category: 'kafka', + description: 'Message part of the trace. ', + name: 'kafka.log.trace.message', + type: 'text', + }, + 'kibana.log.tags': { + category: 'kibana', + description: 'Kibana logging tags. ', + name: 'kibana.log.tags', + type: 'keyword', + }, + 'kibana.log.state': { + category: 'kibana', + description: 'Current state of Kibana. ', + name: 'kibana.log.state', + type: 'keyword', + }, + 'kibana.log.meta': { + category: 'kibana', + name: 'kibana.log.meta', + type: 'object', + }, + 'kibana.log.kibana.log.meta.req.headers.referer': { + category: 'kibana', + name: 'kibana.log.kibana.log.meta.req.headers.referer', + type: 'alias', + }, + 'kibana.log.kibana.log.meta.req.referer': { + category: 'kibana', + name: 'kibana.log.kibana.log.meta.req.referer', + type: 'alias', + }, + 'kibana.log.kibana.log.meta.req.headers.user-agent': { + category: 'kibana', + name: 'kibana.log.kibana.log.meta.req.headers.user-agent', + type: 'alias', + }, + 'kibana.log.kibana.log.meta.req.remoteAddress': { + category: 'kibana', + name: 'kibana.log.kibana.log.meta.req.remoteAddress', + type: 'alias', + }, + 'kibana.log.kibana.log.meta.req.url': { + category: 'kibana', + name: 'kibana.log.kibana.log.meta.req.url', + type: 'alias', + }, + 'kibana.log.kibana.log.meta.statusCode': { + category: 'kibana', + name: 'kibana.log.kibana.log.meta.statusCode', + type: 'alias', + }, + 'kibana.log.kibana.log.meta.method': { + category: 'kibana', + name: 'kibana.log.kibana.log.meta.method', + type: 'alias', + }, + 'logstash.log.module': { + category: 'logstash', + description: 'The module or class where the event originate. ', + name: 'logstash.log.module', + type: 'keyword', + }, + 'logstash.log.thread': { + category: 'logstash', + description: 'Information about the running thread where the log originate. ', + name: 'logstash.log.thread', + type: 'keyword', + }, + 'logstash.log.log_event': { + category: 'logstash', + description: 'key and value debugging information. ', + name: 'logstash.log.log_event', + type: 'object', + }, + 'logstash.log.pipeline_id': { + category: 'logstash', + description: 'The ID of the pipeline. ', + example: 'main', + name: 'logstash.log.pipeline_id', + type: 'keyword', + }, + 'logstash.log.message': { + category: 'logstash', + name: 'logstash.log.message', + type: 'alias', + }, + 'logstash.log.level': { + category: 'logstash', + name: 'logstash.log.level', + type: 'alias', + }, + 'logstash.slowlog.module': { + category: 'logstash', + description: 'The module or class where the event originate. ', + name: 'logstash.slowlog.module', + type: 'keyword', + }, + 'logstash.slowlog.thread': { + category: 'logstash', + description: 'Information about the running thread where the log originate. ', + name: 'logstash.slowlog.thread', + type: 'keyword', + }, + 'logstash.slowlog.event': { + category: 'logstash', + description: 'Raw dump of the original event ', + name: 'logstash.slowlog.event', + type: 'keyword', + }, + 'logstash.slowlog.plugin_name': { + category: 'logstash', + description: 'Name of the plugin ', + name: 'logstash.slowlog.plugin_name', + type: 'keyword', + }, + 'logstash.slowlog.plugin_type': { + category: 'logstash', + description: 'Type of the plugin: Inputs, Filters, Outputs or Codecs. ', + name: 'logstash.slowlog.plugin_type', + type: 'keyword', + }, + 'logstash.slowlog.took_in_millis': { + category: 'logstash', + description: 'Execution time for the plugin in milliseconds. ', + name: 'logstash.slowlog.took_in_millis', + type: 'long', + }, + 'logstash.slowlog.plugin_params': { + category: 'logstash', + description: 'String value of the plugin configuration ', + name: 'logstash.slowlog.plugin_params', + type: 'keyword', + }, + 'logstash.slowlog.plugin_params_object': { + category: 'logstash', + description: 'key -> value of the configuration used by the plugin. ', + name: 'logstash.slowlog.plugin_params_object', + type: 'object', + }, + 'logstash.slowlog.level': { + category: 'logstash', + name: 'logstash.slowlog.level', + type: 'alias', + }, + 'logstash.slowlog.took_in_nanos': { + category: 'logstash', + name: 'logstash.slowlog.took_in_nanos', + type: 'alias', + }, + 'mongodb.log.component': { + category: 'mongodb', + description: 'Functional categorization of message ', + example: 'COMMAND', + name: 'mongodb.log.component', + type: 'keyword', + }, + 'mongodb.log.context': { + category: 'mongodb', + description: 'Context of message ', + example: 'initandlisten', + name: 'mongodb.log.context', + type: 'keyword', + }, + 'mongodb.log.severity': { + category: 'mongodb', + name: 'mongodb.log.severity', + type: 'alias', + }, + 'mongodb.log.message': { + category: 'mongodb', + name: 'mongodb.log.message', + type: 'alias', + }, + 'mysql.thread_id': { + category: 'mysql', + description: 'The connection or thread ID for the query. ', + name: 'mysql.thread_id', + type: 'long', + }, + 'mysql.error.thread_id': { + category: 'mysql', + name: 'mysql.error.thread_id', + type: 'alias', + }, + 'mysql.error.level': { + category: 'mysql', + name: 'mysql.error.level', + type: 'alias', + }, + 'mysql.error.message': { + category: 'mysql', + name: 'mysql.error.message', + type: 'alias', + }, + 'mysql.slowlog.lock_time.sec': { + category: 'mysql', + description: + 'The amount of time the query waited for the lock to be available. The value is in seconds, as a floating point number. ', + name: 'mysql.slowlog.lock_time.sec', + type: 'float', + }, + 'mysql.slowlog.rows_sent': { + category: 'mysql', + description: 'The number of rows returned by the query. ', + name: 'mysql.slowlog.rows_sent', + type: 'long', + }, + 'mysql.slowlog.rows_examined': { + category: 'mysql', + description: 'The number of rows scanned by the query. ', + name: 'mysql.slowlog.rows_examined', + type: 'long', + }, + 'mysql.slowlog.rows_affected': { + category: 'mysql', + description: 'The number of rows modified by the query. ', + name: 'mysql.slowlog.rows_affected', + type: 'long', + }, + 'mysql.slowlog.bytes_sent': { + category: 'mysql', + description: 'The number of bytes sent to client. ', + name: 'mysql.slowlog.bytes_sent', + type: 'long', + format: 'bytes', + }, + 'mysql.slowlog.bytes_received': { + category: 'mysql', + description: 'The number of bytes received from client. ', + name: 'mysql.slowlog.bytes_received', + type: 'long', + format: 'bytes', + }, + 'mysql.slowlog.query': { + category: 'mysql', + description: 'The slow query. ', + name: 'mysql.slowlog.query', + }, + 'mysql.slowlog.id': { + category: 'mysql', + name: 'mysql.slowlog.id', + type: 'alias', + }, + 'mysql.slowlog.schema': { + category: 'mysql', + description: 'The schema where the slow query was executed. ', + name: 'mysql.slowlog.schema', + type: 'keyword', + }, + 'mysql.slowlog.current_user': { + category: 'mysql', + description: + 'Current authenticated user, used to determine access privileges. Can differ from the value for user. ', + name: 'mysql.slowlog.current_user', + type: 'keyword', + }, + 'mysql.slowlog.last_errno': { + category: 'mysql', + description: 'Last SQL error seen. ', + name: 'mysql.slowlog.last_errno', + type: 'keyword', + }, + 'mysql.slowlog.killed': { + category: 'mysql', + description: 'Code of the reason if the query was killed. ', + name: 'mysql.slowlog.killed', + type: 'keyword', + }, + 'mysql.slowlog.query_cache_hit': { + category: 'mysql', + description: 'Whether the query cache was hit. ', + name: 'mysql.slowlog.query_cache_hit', + type: 'boolean', + }, + 'mysql.slowlog.tmp_table': { + category: 'mysql', + description: 'Whether a temporary table was used to resolve the query. ', + name: 'mysql.slowlog.tmp_table', + type: 'boolean', + }, + 'mysql.slowlog.tmp_table_on_disk': { + category: 'mysql', + description: 'Whether the query needed temporary tables on disk. ', + name: 'mysql.slowlog.tmp_table_on_disk', + type: 'boolean', + }, + 'mysql.slowlog.tmp_tables': { + category: 'mysql', + description: 'Number of temporary tables created for this query ', + name: 'mysql.slowlog.tmp_tables', + type: 'long', + }, + 'mysql.slowlog.tmp_disk_tables': { + category: 'mysql', + description: 'Number of temporary tables created on disk for this query. ', + name: 'mysql.slowlog.tmp_disk_tables', + type: 'long', + }, + 'mysql.slowlog.tmp_table_sizes': { + category: 'mysql', + description: 'Size of temporary tables created for this query.', + name: 'mysql.slowlog.tmp_table_sizes', + type: 'long', + format: 'bytes', + }, + 'mysql.slowlog.filesort': { + category: 'mysql', + description: 'Whether filesort optimization was used. ', + name: 'mysql.slowlog.filesort', + type: 'boolean', + }, + 'mysql.slowlog.filesort_on_disk': { + category: 'mysql', + description: 'Whether filesort optimization was used and it needed temporary tables on disk. ', + name: 'mysql.slowlog.filesort_on_disk', + type: 'boolean', + }, + 'mysql.slowlog.priority_queue': { + category: 'mysql', + description: 'Whether a priority queue was used for filesort. ', + name: 'mysql.slowlog.priority_queue', + type: 'boolean', + }, + 'mysql.slowlog.full_scan': { + category: 'mysql', + description: 'Whether a full table scan was needed for the slow query. ', + name: 'mysql.slowlog.full_scan', + type: 'boolean', + }, + 'mysql.slowlog.full_join': { + category: 'mysql', + description: + 'Whether a full join was needed for the slow query (no indexes were used for joins). ', + name: 'mysql.slowlog.full_join', + type: 'boolean', + }, + 'mysql.slowlog.merge_passes': { + category: 'mysql', + description: 'Number of merge passes executed for the query. ', + name: 'mysql.slowlog.merge_passes', + type: 'long', + }, + 'mysql.slowlog.sort_merge_passes': { + category: 'mysql', + description: 'Number of merge passes that the sort algorithm has had to do. ', + name: 'mysql.slowlog.sort_merge_passes', + type: 'long', + }, + 'mysql.slowlog.sort_range_count': { + category: 'mysql', + description: 'Number of sorts that were done using ranges. ', + name: 'mysql.slowlog.sort_range_count', + type: 'long', + }, + 'mysql.slowlog.sort_rows': { + category: 'mysql', + description: 'Number of sorted rows. ', + name: 'mysql.slowlog.sort_rows', + type: 'long', + }, + 'mysql.slowlog.sort_scan_count': { + category: 'mysql', + description: 'Number of sorts that were done by scanning the table. ', + name: 'mysql.slowlog.sort_scan_count', + type: 'long', + }, + 'mysql.slowlog.log_slow_rate_type': { + category: 'mysql', + description: + 'Type of slow log rate limit, it can be `session` if the rate limit is applied per session, or `query` if it applies per query. ', + name: 'mysql.slowlog.log_slow_rate_type', + type: 'keyword', + }, + 'mysql.slowlog.log_slow_rate_limit': { + category: 'mysql', + description: + 'Slow log rate limit, a value of 100 means that one in a hundred queries or sessions are being logged. ', + name: 'mysql.slowlog.log_slow_rate_limit', + type: 'keyword', + }, + 'mysql.slowlog.read_first': { + category: 'mysql', + description: 'The number of times the first entry in an index was read. ', + name: 'mysql.slowlog.read_first', + type: 'long', + }, + 'mysql.slowlog.read_last': { + category: 'mysql', + description: 'The number of times the last key in an index was read. ', + name: 'mysql.slowlog.read_last', + type: 'long', + }, + 'mysql.slowlog.read_key': { + category: 'mysql', + description: 'The number of requests to read a row based on a key. ', + name: 'mysql.slowlog.read_key', + type: 'long', + }, + 'mysql.slowlog.read_next': { + category: 'mysql', + description: 'The number of requests to read the next row in key order. ', + name: 'mysql.slowlog.read_next', + type: 'long', + }, + 'mysql.slowlog.read_prev': { + category: 'mysql', + description: 'The number of requests to read the previous row in key order. ', + name: 'mysql.slowlog.read_prev', + type: 'long', + }, + 'mysql.slowlog.read_rnd': { + category: 'mysql', + description: 'The number of requests to read a row based on a fixed position. ', + name: 'mysql.slowlog.read_rnd', + type: 'long', + }, + 'mysql.slowlog.read_rnd_next': { + category: 'mysql', + description: 'The number of requests to read the next row in the data file. ', + name: 'mysql.slowlog.read_rnd_next', + type: 'long', + }, + 'mysql.slowlog.innodb.trx_id': { + category: 'mysql', + description: 'Transaction ID ', + name: 'mysql.slowlog.innodb.trx_id', + type: 'keyword', + }, + 'mysql.slowlog.innodb.io_r_ops': { + category: 'mysql', + description: 'Number of page read operations. ', + name: 'mysql.slowlog.innodb.io_r_ops', + type: 'long', + }, + 'mysql.slowlog.innodb.io_r_bytes': { + category: 'mysql', + description: 'Bytes read during page read operations. ', + name: 'mysql.slowlog.innodb.io_r_bytes', + type: 'long', + format: 'bytes', + }, + 'mysql.slowlog.innodb.io_r_wait.sec': { + category: 'mysql', + description: 'How long it took to read all needed data from storage. ', + name: 'mysql.slowlog.innodb.io_r_wait.sec', + type: 'long', + }, + 'mysql.slowlog.innodb.rec_lock_wait.sec': { + category: 'mysql', + description: 'How long the query waited for locks. ', + name: 'mysql.slowlog.innodb.rec_lock_wait.sec', + type: 'long', + }, + 'mysql.slowlog.innodb.queue_wait.sec': { + category: 'mysql', + description: + 'How long the query waited to enter the InnoDB queue and to be executed once in the queue. ', + name: 'mysql.slowlog.innodb.queue_wait.sec', + type: 'long', + }, + 'mysql.slowlog.innodb.pages_distinct': { + category: 'mysql', + description: 'Approximated count of pages accessed to execute the query. ', + name: 'mysql.slowlog.innodb.pages_distinct', + type: 'long', + }, + 'mysql.slowlog.user': { + category: 'mysql', + name: 'mysql.slowlog.user', + type: 'alias', + }, + 'mysql.slowlog.host': { + category: 'mysql', + name: 'mysql.slowlog.host', + type: 'alias', + }, + 'mysql.slowlog.ip': { + category: 'mysql', + name: 'mysql.slowlog.ip', + type: 'alias', + }, + 'nats.log.client.id': { + category: 'nats', + description: 'The id of the client ', + name: 'nats.log.client.id', + type: 'integer', + }, + 'nats.log.msg.bytes': { + category: 'nats', + description: 'Size of the payload in bytes ', + name: 'nats.log.msg.bytes', + type: 'long', + format: 'bytes', + }, + 'nats.log.msg.type': { + category: 'nats', + description: 'The protocol message type ', + name: 'nats.log.msg.type', + type: 'keyword', + }, + 'nats.log.msg.subject': { + category: 'nats', + description: 'Subject name this message was received on ', + name: 'nats.log.msg.subject', + type: 'keyword', + }, + 'nats.log.msg.sid': { + category: 'nats', + description: 'The unique alphanumeric subscription ID of the subject ', + name: 'nats.log.msg.sid', + type: 'integer', + }, + 'nats.log.msg.reply_to': { + category: 'nats', + description: 'The inbox subject on which the publisher is listening for responses ', + name: 'nats.log.msg.reply_to', + type: 'keyword', + }, + 'nats.log.msg.max_messages': { + category: 'nats', + description: 'An optional number of messages to wait for before automatically unsubscribing ', + name: 'nats.log.msg.max_messages', + type: 'integer', + }, + 'nats.log.msg.error.message': { + category: 'nats', + description: 'Details about the error occurred ', + name: 'nats.log.msg.error.message', + type: 'text', + }, + 'nats.log.msg.queue_group': { + category: 'nats', + description: 'The queue group which subscriber will join ', + name: 'nats.log.msg.queue_group', + type: 'text', + }, + 'nginx.access.remote_ip_list': { + category: 'nginx', + description: + 'An array of remote IP addresses. It is a list because it is common to include, besides the client IP address, IP addresses from headers like `X-Forwarded-For`. Real source IP is restored to `source.ip`. ', + name: 'nginx.access.remote_ip_list', + type: 'array', + }, + 'nginx.access.body_sent.bytes': { + category: 'nginx', + name: 'nginx.access.body_sent.bytes', + type: 'alias', + }, + 'nginx.access.user_name': { + category: 'nginx', + name: 'nginx.access.user_name', + type: 'alias', + }, + 'nginx.access.method': { + category: 'nginx', + name: 'nginx.access.method', + type: 'alias', + }, + 'nginx.access.url': { + category: 'nginx', + name: 'nginx.access.url', + type: 'alias', + }, + 'nginx.access.http_version': { + category: 'nginx', + name: 'nginx.access.http_version', + type: 'alias', + }, + 'nginx.access.response_code': { + category: 'nginx', + name: 'nginx.access.response_code', + type: 'alias', + }, + 'nginx.access.referrer': { + category: 'nginx', + name: 'nginx.access.referrer', + type: 'alias', + }, + 'nginx.access.agent': { + category: 'nginx', + name: 'nginx.access.agent', + type: 'alias', + }, + 'nginx.access.user_agent.device': { + category: 'nginx', + name: 'nginx.access.user_agent.device', + type: 'alias', + }, + 'nginx.access.user_agent.name': { + category: 'nginx', + name: 'nginx.access.user_agent.name', + type: 'alias', + }, + 'nginx.access.user_agent.os': { + category: 'nginx', + name: 'nginx.access.user_agent.os', + type: 'alias', + }, + 'nginx.access.user_agent.os_name': { + category: 'nginx', + name: 'nginx.access.user_agent.os_name', + type: 'alias', + }, + 'nginx.access.user_agent.original': { + category: 'nginx', + name: 'nginx.access.user_agent.original', + type: 'alias', + }, + 'nginx.access.geoip.continent_name': { + category: 'nginx', + name: 'nginx.access.geoip.continent_name', + type: 'alias', + }, + 'nginx.access.geoip.country_iso_code': { + category: 'nginx', + name: 'nginx.access.geoip.country_iso_code', + type: 'alias', + }, + 'nginx.access.geoip.location': { + category: 'nginx', + name: 'nginx.access.geoip.location', + type: 'alias', + }, + 'nginx.access.geoip.region_name': { + category: 'nginx', + name: 'nginx.access.geoip.region_name', + type: 'alias', + }, + 'nginx.access.geoip.city_name': { + category: 'nginx', + name: 'nginx.access.geoip.city_name', + type: 'alias', + }, + 'nginx.access.geoip.region_iso_code': { + category: 'nginx', + name: 'nginx.access.geoip.region_iso_code', + type: 'alias', + }, + 'nginx.error.connection_id': { + category: 'nginx', + description: 'Connection identifier. ', + name: 'nginx.error.connection_id', + type: 'long', + }, + 'nginx.error.level': { + category: 'nginx', + name: 'nginx.error.level', + type: 'alias', + }, + 'nginx.error.pid': { + category: 'nginx', + name: 'nginx.error.pid', + type: 'alias', + }, + 'nginx.error.tid': { + category: 'nginx', + name: 'nginx.error.tid', + type: 'alias', + }, + 'nginx.error.message': { + category: 'nginx', + name: 'nginx.error.message', + type: 'alias', + }, + 'nginx.ingress_controller.remote_ip_list': { + category: 'nginx', + description: + 'An array of remote IP addresses. It is a list because it is common to include, besides the client IP address, IP addresses from headers like `X-Forwarded-For`. Real source IP is restored to `source.ip`. ', + name: 'nginx.ingress_controller.remote_ip_list', + type: 'array', + }, + 'nginx.ingress_controller.http.request.length': { + category: 'nginx', + description: 'The request length (including request line, header, and request body) ', + name: 'nginx.ingress_controller.http.request.length', + type: 'long', + format: 'bytes', + }, + 'nginx.ingress_controller.http.request.time': { + category: 'nginx', + description: 'Time elapsed since the first bytes were read from the client ', + name: 'nginx.ingress_controller.http.request.time', + type: 'double', + format: 'duration', + }, + 'nginx.ingress_controller.upstream.name': { + category: 'nginx', + description: 'The name of the upstream. ', + name: 'nginx.ingress_controller.upstream.name', + type: 'keyword', + }, + 'nginx.ingress_controller.upstream.alternative_name': { + category: 'nginx', + description: 'The name of the alternative upstream. ', + name: 'nginx.ingress_controller.upstream.alternative_name', + type: 'keyword', + }, + 'nginx.ingress_controller.upstream.response.length': { + category: 'nginx', + description: 'The length of the response obtained from the upstream server ', + name: 'nginx.ingress_controller.upstream.response.length', + type: 'long', + format: 'bytes', + }, + 'nginx.ingress_controller.upstream.response.time': { + category: 'nginx', + description: + 'The time spent on receiving the response from the upstream server as seconds with millisecond resolution ', + name: 'nginx.ingress_controller.upstream.response.time', + type: 'double', + format: 'duration', + }, + 'nginx.ingress_controller.upstream.response.status_code': { + category: 'nginx', + description: 'The status code of the response obtained from the upstream server ', + name: 'nginx.ingress_controller.upstream.response.status_code', + type: 'long', + }, + 'nginx.ingress_controller.http.request.id': { + category: 'nginx', + description: 'The randomly generated ID of the request ', + name: 'nginx.ingress_controller.http.request.id', + type: 'keyword', + }, + 'nginx.ingress_controller.upstream.ip': { + category: 'nginx', + description: + 'The IP address of the upstream server. If several servers were contacted during request processing, their addresses are separated by commas. ', + name: 'nginx.ingress_controller.upstream.ip', + type: 'ip', + }, + 'nginx.ingress_controller.upstream.port': { + category: 'nginx', + description: 'The port of the upstream server. ', + name: 'nginx.ingress_controller.upstream.port', + type: 'long', + }, + 'nginx.ingress_controller.body_sent.bytes': { + category: 'nginx', + name: 'nginx.ingress_controller.body_sent.bytes', + type: 'alias', + }, + 'nginx.ingress_controller.user_name': { + category: 'nginx', + name: 'nginx.ingress_controller.user_name', + type: 'alias', + }, + 'nginx.ingress_controller.method': { + category: 'nginx', + name: 'nginx.ingress_controller.method', + type: 'alias', + }, + 'nginx.ingress_controller.url': { + category: 'nginx', + name: 'nginx.ingress_controller.url', + type: 'alias', + }, + 'nginx.ingress_controller.http_version': { + category: 'nginx', + name: 'nginx.ingress_controller.http_version', + type: 'alias', + }, + 'nginx.ingress_controller.response_code': { + category: 'nginx', + name: 'nginx.ingress_controller.response_code', + type: 'alias', + }, + 'nginx.ingress_controller.referrer': { + category: 'nginx', + name: 'nginx.ingress_controller.referrer', + type: 'alias', + }, + 'nginx.ingress_controller.agent': { + category: 'nginx', + name: 'nginx.ingress_controller.agent', + type: 'alias', + }, + 'nginx.ingress_controller.user_agent.device': { + category: 'nginx', + name: 'nginx.ingress_controller.user_agent.device', + type: 'alias', + }, + 'nginx.ingress_controller.user_agent.name': { + category: 'nginx', + name: 'nginx.ingress_controller.user_agent.name', + type: 'alias', + }, + 'nginx.ingress_controller.user_agent.os': { + category: 'nginx', + name: 'nginx.ingress_controller.user_agent.os', + type: 'alias', + }, + 'nginx.ingress_controller.user_agent.os_name': { + category: 'nginx', + name: 'nginx.ingress_controller.user_agent.os_name', + type: 'alias', + }, + 'nginx.ingress_controller.user_agent.original': { + category: 'nginx', + name: 'nginx.ingress_controller.user_agent.original', + type: 'alias', + }, + 'nginx.ingress_controller.geoip.continent_name': { + category: 'nginx', + name: 'nginx.ingress_controller.geoip.continent_name', + type: 'alias', + }, + 'nginx.ingress_controller.geoip.country_iso_code': { + category: 'nginx', + name: 'nginx.ingress_controller.geoip.country_iso_code', + type: 'alias', + }, + 'nginx.ingress_controller.geoip.location': { + category: 'nginx', + name: 'nginx.ingress_controller.geoip.location', + type: 'alias', + }, + 'nginx.ingress_controller.geoip.region_name': { + category: 'nginx', + name: 'nginx.ingress_controller.geoip.region_name', + type: 'alias', + }, + 'nginx.ingress_controller.geoip.city_name': { + category: 'nginx', + name: 'nginx.ingress_controller.geoip.city_name', + type: 'alias', + }, + 'nginx.ingress_controller.geoip.region_iso_code': { + category: 'nginx', + name: 'nginx.ingress_controller.geoip.region_iso_code', + type: 'alias', + }, + 'osquery.result.name': { + category: 'osquery', + description: 'The name of the query that generated this event. ', + name: 'osquery.result.name', + type: 'keyword', + }, + 'osquery.result.action': { + category: 'osquery', + description: + 'For incremental data, marks whether the entry was added or removed. It can be one of "added", "removed", or "snapshot". ', + name: 'osquery.result.action', + type: 'keyword', + }, + 'osquery.result.host_identifier': { + category: 'osquery', + description: + 'The identifier for the host on which the osquery agent is running. Normally the hostname. ', + name: 'osquery.result.host_identifier', + type: 'keyword', + }, + 'osquery.result.unix_time': { + category: 'osquery', + description: + 'Unix timestamp of the event, in seconds since the epoch. Used for computing the `@timestamp` column. ', + name: 'osquery.result.unix_time', + type: 'long', + }, + 'osquery.result.calendar_time': { + category: 'osquery', + description: 'String representation of the collection time, as formatted by osquery. ', + name: 'osquery.result.calendar_time', + type: 'keyword', + }, + 'postgresql.log.timestamp': { + category: 'postgresql', + description: 'The timestamp from the log line. ', + name: 'postgresql.log.timestamp', + }, + 'postgresql.log.core_id': { + category: 'postgresql', + description: 'Core id ', + name: 'postgresql.log.core_id', + type: 'long', + }, + 'postgresql.log.database': { + category: 'postgresql', + description: 'Name of database ', + example: 'mydb', + name: 'postgresql.log.database', + }, + 'postgresql.log.query': { + category: 'postgresql', + description: 'Query statement. ', + example: 'SELECT * FROM users;', + name: 'postgresql.log.query', + }, + 'postgresql.log.query_step': { + category: 'postgresql', + description: + 'Statement step when using extended query protocol (one of statement, parse, bind or execute) ', + example: 'parse', + name: 'postgresql.log.query_step', + }, + 'postgresql.log.query_name': { + category: 'postgresql', + description: + 'Name given to a query when using extended query protocol. If it is "", or not present, this field is ignored. ', + example: 'pdo_stmt_00000001', + name: 'postgresql.log.query_name', + }, + 'postgresql.log.error.code': { + category: 'postgresql', + description: 'Error code returned by Postgres (if any)', + name: 'postgresql.log.error.code', + type: 'long', + }, + 'postgresql.log.timezone': { + category: 'postgresql', + name: 'postgresql.log.timezone', + type: 'alias', + }, + 'postgresql.log.thread_id': { + category: 'postgresql', + name: 'postgresql.log.thread_id', + type: 'alias', + }, + 'postgresql.log.user': { + category: 'postgresql', + name: 'postgresql.log.user', + type: 'alias', + }, + 'postgresql.log.level': { + category: 'postgresql', + name: 'postgresql.log.level', + type: 'alias', + }, + 'postgresql.log.message': { + category: 'postgresql', + name: 'postgresql.log.message', + type: 'alias', + }, + 'redis.log.role': { + category: 'redis', + description: + 'The role of the Redis instance. Can be one of `master`, `slave`, `child` (for RDF/AOF writing child), or `sentinel`. ', + name: 'redis.log.role', + type: 'keyword', + }, + 'redis.log.pid': { + category: 'redis', + name: 'redis.log.pid', + type: 'alias', + }, + 'redis.log.level': { + category: 'redis', + name: 'redis.log.level', + type: 'alias', + }, + 'redis.log.message': { + category: 'redis', + name: 'redis.log.message', + type: 'alias', + }, + 'redis.slowlog.cmd': { + category: 'redis', + description: 'The command executed. ', + name: 'redis.slowlog.cmd', + type: 'keyword', + }, + 'redis.slowlog.duration.us': { + category: 'redis', + description: 'How long it took to execute the command in microseconds. ', + name: 'redis.slowlog.duration.us', + type: 'long', + }, + 'redis.slowlog.id': { + category: 'redis', + description: 'The ID of the query. ', + name: 'redis.slowlog.id', + type: 'long', + }, + 'redis.slowlog.key': { + category: 'redis', + description: 'The key on which the command was executed. ', + name: 'redis.slowlog.key', + type: 'keyword', + }, + 'redis.slowlog.args': { + category: 'redis', + description: 'The arguments with which the command was called. ', + name: 'redis.slowlog.args', + type: 'keyword', + }, + 'santa.action': { + category: 'santa', + description: 'Action', + example: 'EXEC', + name: 'santa.action', + type: 'keyword', + }, + 'santa.decision': { + category: 'santa', + description: 'Decision that santad took.', + example: 'ALLOW', + name: 'santa.decision', + type: 'keyword', + }, + 'santa.reason': { + category: 'santa', + description: 'Reason for the decsision.', + example: 'CERT', + name: 'santa.reason', + type: 'keyword', + }, + 'santa.mode': { + category: 'santa', + description: 'Operating mode of Santa.', + example: 'M', + name: 'santa.mode', + type: 'keyword', + }, + 'santa.disk.volume': { + category: 'santa', + description: 'The volume name.', + name: 'santa.disk.volume', + }, + 'santa.disk.bus': { + category: 'santa', + description: 'The disk bus protocol.', + name: 'santa.disk.bus', + }, + 'santa.disk.serial': { + category: 'santa', + description: 'The disk serial number.', + name: 'santa.disk.serial', + }, + 'santa.disk.bsdname': { + category: 'santa', + description: 'The disk BSD name.', + example: 'disk1s3', + name: 'santa.disk.bsdname', + }, + 'santa.disk.model': { + category: 'santa', + description: 'The disk model.', + example: 'APPLE SSD SM0512L', + name: 'santa.disk.model', + }, + 'santa.disk.fs': { + category: 'santa', + description: 'The disk volume kind (filesystem type).', + example: 'apfs', + name: 'santa.disk.fs', + }, + 'santa.disk.mount': { + category: 'santa', + description: 'The disk volume path.', + name: 'santa.disk.mount', + }, + 'santa.certificate.common_name': { + category: 'santa', + description: 'Common name from code signing certificate.', + name: 'santa.certificate.common_name', + type: 'keyword', + }, + 'santa.certificate.sha256': { + category: 'santa', + description: 'SHA256 hash of code signing certificate.', + name: 'santa.certificate.sha256', + type: 'keyword', + }, + 'system.auth.timestamp': { + category: 'system', + name: 'system.auth.timestamp', + type: 'alias', + }, + 'system.auth.hostname': { + category: 'system', + name: 'system.auth.hostname', + type: 'alias', + }, + 'system.auth.program': { + category: 'system', + name: 'system.auth.program', + type: 'alias', + }, + 'system.auth.pid': { + category: 'system', + name: 'system.auth.pid', + type: 'alias', + }, + 'system.auth.message': { + category: 'system', + name: 'system.auth.message', + type: 'alias', + }, + 'system.auth.user': { + category: 'system', + name: 'system.auth.user', + type: 'alias', + }, + 'system.auth.ssh.method': { + category: 'system', + description: 'The SSH authentication method. Can be one of "password" or "publickey". ', + name: 'system.auth.ssh.method', + }, + 'system.auth.ssh.signature': { + category: 'system', + description: 'The signature of the client public key. ', + name: 'system.auth.ssh.signature', + }, + 'system.auth.ssh.dropped_ip': { + category: 'system', + description: 'The client IP from SSH connections that are open and immediately dropped. ', + name: 'system.auth.ssh.dropped_ip', + type: 'ip', + }, + 'system.auth.ssh.event': { + category: 'system', + description: 'The SSH event as found in the logs (Accepted, Invalid, Failed, etc.) ', + example: 'Accepted', + name: 'system.auth.ssh.event', + }, + 'system.auth.ssh.ip': { + category: 'system', + name: 'system.auth.ssh.ip', + type: 'alias', + }, + 'system.auth.ssh.port': { + category: 'system', + name: 'system.auth.ssh.port', + type: 'alias', + }, + 'system.auth.ssh.geoip.continent_name': { + category: 'system', + name: 'system.auth.ssh.geoip.continent_name', + type: 'alias', + }, + 'system.auth.ssh.geoip.country_iso_code': { + category: 'system', + name: 'system.auth.ssh.geoip.country_iso_code', + type: 'alias', + }, + 'system.auth.ssh.geoip.location': { + category: 'system', + name: 'system.auth.ssh.geoip.location', + type: 'alias', + }, + 'system.auth.ssh.geoip.region_name': { + category: 'system', + name: 'system.auth.ssh.geoip.region_name', + type: 'alias', + }, + 'system.auth.ssh.geoip.city_name': { + category: 'system', + name: 'system.auth.ssh.geoip.city_name', + type: 'alias', + }, + 'system.auth.ssh.geoip.region_iso_code': { + category: 'system', + name: 'system.auth.ssh.geoip.region_iso_code', + type: 'alias', + }, + 'system.auth.sudo.error': { + category: 'system', + description: 'The error message in case the sudo command failed. ', + example: 'user NOT in sudoers', + name: 'system.auth.sudo.error', + }, + 'system.auth.sudo.tty': { + category: 'system', + description: 'The TTY where the sudo command is executed. ', + name: 'system.auth.sudo.tty', + }, + 'system.auth.sudo.pwd': { + category: 'system', + description: 'The current directory where the sudo command is executed. ', + name: 'system.auth.sudo.pwd', + }, + 'system.auth.sudo.user': { + category: 'system', + description: 'The target user to which the sudo command is switching. ', + example: 'root', + name: 'system.auth.sudo.user', + }, + 'system.auth.sudo.command': { + category: 'system', + description: 'The command executed via sudo. ', + name: 'system.auth.sudo.command', + }, + 'system.auth.useradd.home': { + category: 'system', + description: 'The home folder for the new user.', + name: 'system.auth.useradd.home', + }, + 'system.auth.useradd.shell': { + category: 'system', + description: 'The default shell for the new user.', + name: 'system.auth.useradd.shell', + }, + 'system.auth.useradd.name': { + category: 'system', + name: 'system.auth.useradd.name', + type: 'alias', + }, + 'system.auth.useradd.uid': { + category: 'system', + name: 'system.auth.useradd.uid', + type: 'alias', + }, + 'system.auth.useradd.gid': { + category: 'system', + name: 'system.auth.useradd.gid', + type: 'alias', + }, + 'system.auth.groupadd.name': { + category: 'system', + name: 'system.auth.groupadd.name', + type: 'alias', + }, + 'system.auth.groupadd.gid': { + category: 'system', + name: 'system.auth.groupadd.gid', + type: 'alias', + }, + 'system.syslog.timestamp': { + category: 'system', + name: 'system.syslog.timestamp', + type: 'alias', + }, + 'system.syslog.hostname': { + category: 'system', + name: 'system.syslog.hostname', + type: 'alias', + }, + 'system.syslog.program': { + category: 'system', + name: 'system.syslog.program', + type: 'alias', + }, + 'system.syslog.pid': { + category: 'system', + name: 'system.syslog.pid', + type: 'alias', + }, + 'system.syslog.message': { + category: 'system', + name: 'system.syslog.message', + type: 'alias', + }, + 'traefik.access.user_identifier': { + category: 'traefik', + description: 'Is the RFC 1413 identity of the client ', + name: 'traefik.access.user_identifier', + type: 'keyword', + }, + 'traefik.access.request_count': { + category: 'traefik', + description: 'The number of requests ', + name: 'traefik.access.request_count', + type: 'long', + }, + 'traefik.access.frontend_name': { + category: 'traefik', + description: 'The name of the frontend used ', + name: 'traefik.access.frontend_name', + type: 'keyword', + }, + 'traefik.access.backend_url': { + category: 'traefik', + description: 'The url of the backend where request is forwarded', + name: 'traefik.access.backend_url', + type: 'keyword', + }, + 'traefik.access.body_sent.bytes': { + category: 'traefik', + name: 'traefik.access.body_sent.bytes', + type: 'alias', + }, + 'traefik.access.remote_ip': { + category: 'traefik', + name: 'traefik.access.remote_ip', + type: 'alias', + }, + 'traefik.access.user_name': { + category: 'traefik', + name: 'traefik.access.user_name', + type: 'alias', + }, + 'traefik.access.method': { + category: 'traefik', + name: 'traefik.access.method', + type: 'alias', + }, + 'traefik.access.url': { + category: 'traefik', + name: 'traefik.access.url', + type: 'alias', + }, + 'traefik.access.http_version': { + category: 'traefik', + name: 'traefik.access.http_version', + type: 'alias', + }, + 'traefik.access.response_code': { + category: 'traefik', + name: 'traefik.access.response_code', + type: 'alias', + }, + 'traefik.access.referrer': { + category: 'traefik', + name: 'traefik.access.referrer', + type: 'alias', + }, + 'traefik.access.agent': { + category: 'traefik', + name: 'traefik.access.agent', + type: 'alias', + }, + 'traefik.access.user_agent.device': { + category: 'traefik', + name: 'traefik.access.user_agent.device', + type: 'alias', + }, + 'traefik.access.user_agent.name': { + category: 'traefik', + name: 'traefik.access.user_agent.name', + type: 'alias', + }, + 'traefik.access.user_agent.os': { + category: 'traefik', + name: 'traefik.access.user_agent.os', + type: 'alias', + }, + 'traefik.access.user_agent.os_name': { + category: 'traefik', + name: 'traefik.access.user_agent.os_name', + type: 'alias', + }, + 'traefik.access.user_agent.original': { + category: 'traefik', + name: 'traefik.access.user_agent.original', + type: 'alias', + }, + 'traefik.access.geoip.continent_name': { + category: 'traefik', + name: 'traefik.access.geoip.continent_name', + type: 'alias', + }, + 'traefik.access.geoip.country_iso_code': { + category: 'traefik', + name: 'traefik.access.geoip.country_iso_code', + type: 'alias', + }, + 'traefik.access.geoip.location': { + category: 'traefik', + name: 'traefik.access.geoip.location', + type: 'alias', + }, + 'traefik.access.geoip.region_name': { + category: 'traefik', + name: 'traefik.access.geoip.region_name', + type: 'alias', + }, + 'traefik.access.geoip.city_name': { + category: 'traefik', + name: 'traefik.access.geoip.city_name', + type: 'alias', + }, + 'traefik.access.geoip.region_iso_code': { + category: 'traefik', + name: 'traefik.access.geoip.region_iso_code', + type: 'alias', + }, + 'activemq.caller': { + category: 'activemq', + description: 'Name of the caller issuing the logging request (class or resource). ', + name: 'activemq.caller', + type: 'keyword', + }, + 'activemq.thread': { + category: 'activemq', + description: 'Thread that generated the logging event. ', + name: 'activemq.thread', + type: 'keyword', + }, + 'activemq.user': { + category: 'activemq', + description: 'User that generated the logging event. ', + name: 'activemq.user', + type: 'keyword', + }, + 'activemq.audit': { + category: 'activemq', + description: 'Fields from ActiveMQ audit logs. ', + name: 'activemq.audit', + type: 'group', + }, + 'activemq.log.stack_trace': { + category: 'activemq', + name: 'activemq.log.stack_trace', + type: 'keyword', + }, + 'aws.cloudtrail.event_version': { + category: 'aws', + description: 'The CloudTrail version of the log event format. ', + name: 'aws.cloudtrail.event_version', + type: 'keyword', + }, + 'aws.cloudtrail.user_identity.type': { + category: 'aws', + description: 'The type of the identity ', + name: 'aws.cloudtrail.user_identity.type', + type: 'keyword', + }, + 'aws.cloudtrail.user_identity.arn': { + category: 'aws', + description: 'The Amazon Resource Name (ARN) of the principal that made the call.', + name: 'aws.cloudtrail.user_identity.arn', + type: 'keyword', + }, + 'aws.cloudtrail.user_identity.access_key_id': { + category: 'aws', + description: 'The access key ID that was used to sign the request.', + name: 'aws.cloudtrail.user_identity.access_key_id', + type: 'keyword', + }, + 'aws.cloudtrail.user_identity.session_context.mfa_authenticated': { + category: 'aws', + description: + 'The value is true if the root user or IAM user whose credentials were used for the request also was authenticated with an MFA device; otherwise, false.', + name: 'aws.cloudtrail.user_identity.session_context.mfa_authenticated', + type: 'keyword', + }, + 'aws.cloudtrail.user_identity.session_context.creation_date': { + category: 'aws', + description: 'The date and time when the temporary security credentials were issued.', + name: 'aws.cloudtrail.user_identity.session_context.creation_date', + type: 'date', + }, + 'aws.cloudtrail.user_identity.session_context.session_issuer.type': { + category: 'aws', + description: + 'The source of the temporary security credentials, such as Root, IAMUser, or Role.', + name: 'aws.cloudtrail.user_identity.session_context.session_issuer.type', + type: 'keyword', + }, + 'aws.cloudtrail.user_identity.session_context.session_issuer.principal_id': { + category: 'aws', + description: 'The internal ID of the entity that was used to get credentials.', + name: 'aws.cloudtrail.user_identity.session_context.session_issuer.principal_id', + type: 'keyword', + }, + 'aws.cloudtrail.user_identity.session_context.session_issuer.arn': { + category: 'aws', + description: + 'The ARN of the source (account, IAM user, or role) that was used to get temporary security credentials.', + name: 'aws.cloudtrail.user_identity.session_context.session_issuer.arn', + type: 'keyword', + }, + 'aws.cloudtrail.user_identity.session_context.session_issuer.account_id': { + category: 'aws', + description: 'The account that owns the entity that was used to get credentials.', + name: 'aws.cloudtrail.user_identity.session_context.session_issuer.account_id', + type: 'keyword', + }, + 'aws.cloudtrail.user_identity.invoked_by': { + category: 'aws', + description: + 'The name of the AWS service that made the request, such as Amazon EC2 Auto Scaling or AWS Elastic Beanstalk.', + name: 'aws.cloudtrail.user_identity.invoked_by', + type: 'keyword', + }, + 'aws.cloudtrail.error_code': { + category: 'aws', + description: 'The AWS service error if the request returns an error.', + name: 'aws.cloudtrail.error_code', + type: 'keyword', + }, + 'aws.cloudtrail.error_message': { + category: 'aws', + description: 'If the request returns an error, the description of the error.', + name: 'aws.cloudtrail.error_message', + type: 'keyword', + }, + 'aws.cloudtrail.request_parameters': { + category: 'aws', + description: 'The parameters, if any, that were sent with the request.', + name: 'aws.cloudtrail.request_parameters', + type: 'keyword', + }, + 'aws.cloudtrail.response_elements': { + category: 'aws', + description: + 'The response element for actions that make changes (create, update, or delete actions).', + name: 'aws.cloudtrail.response_elements', + type: 'keyword', + }, + 'aws.cloudtrail.additional_eventdata': { + category: 'aws', + description: 'Additional data about the event that was not part of the request or response.', + name: 'aws.cloudtrail.additional_eventdata', + type: 'keyword', + }, + 'aws.cloudtrail.request_id': { + category: 'aws', + description: + 'The value that identifies the request. The service being called generates this value.', + name: 'aws.cloudtrail.request_id', + type: 'keyword', + }, + 'aws.cloudtrail.event_type': { + category: 'aws', + description: 'Identifies the type of event that generated the event record.', + name: 'aws.cloudtrail.event_type', + type: 'keyword', + }, + 'aws.cloudtrail.api_version': { + category: 'aws', + description: 'Identifies the API version associated with the AwsApiCall eventType value.', + name: 'aws.cloudtrail.api_version', + type: 'keyword', + }, + 'aws.cloudtrail.management_event': { + category: 'aws', + description: 'A Boolean value that identifies whether the event is a management event.', + name: 'aws.cloudtrail.management_event', + type: 'keyword', + }, + 'aws.cloudtrail.read_only': { + category: 'aws', + description: 'Identifies whether this operation is a read-only operation.', + name: 'aws.cloudtrail.read_only', + type: 'keyword', + }, + 'aws.cloudtrail.resources.arn': { + category: 'aws', + description: 'Resource ARNs', + name: 'aws.cloudtrail.resources.arn', + type: 'keyword', + }, + 'aws.cloudtrail.resources.account_id': { + category: 'aws', + description: 'Account ID of the resource owner', + name: 'aws.cloudtrail.resources.account_id', + type: 'keyword', + }, + 'aws.cloudtrail.resources.type': { + category: 'aws', + description: 'Resource type identifier in the format: AWS::aws-service-name::data-type-name', + name: 'aws.cloudtrail.resources.type', + type: 'keyword', + }, + 'aws.cloudtrail.recipient_account_id': { + category: 'aws', + description: 'Represents the account ID that received this event.', + name: 'aws.cloudtrail.recipient_account_id', + type: 'keyword', + }, + 'aws.cloudtrail.service_event_details': { + category: 'aws', + description: 'Identifies the service event, including what triggered the event and the result.', + name: 'aws.cloudtrail.service_event_details', + type: 'keyword', + }, + 'aws.cloudtrail.shared_event_id': { + category: 'aws', + description: + 'GUID generated by CloudTrail to uniquely identify CloudTrail events from the same AWS action that is sent to different AWS accounts.', + name: 'aws.cloudtrail.shared_event_id', + type: 'keyword', + }, + 'aws.cloudtrail.vpc_endpoint_id': { + category: 'aws', + description: + 'Identifies the VPC endpoint in which requests were made from a VPC to another AWS service, such as Amazon S3.', + name: 'aws.cloudtrail.vpc_endpoint_id', + type: 'keyword', + }, + 'aws.cloudtrail.console_login.additional_eventdata.mobile_version': { + category: 'aws', + description: 'Identifies whether ConsoleLogin was from mobile version', + name: 'aws.cloudtrail.console_login.additional_eventdata.mobile_version', + type: 'boolean', + }, + 'aws.cloudtrail.console_login.additional_eventdata.login_to': { + category: 'aws', + description: 'URL for ConsoleLogin', + name: 'aws.cloudtrail.console_login.additional_eventdata.login_to', + type: 'keyword', + }, + 'aws.cloudtrail.console_login.additional_eventdata.mfa_used': { + category: 'aws', + description: 'Identifies whether multi factor authentication was used during ConsoleLogin', + name: 'aws.cloudtrail.console_login.additional_eventdata.mfa_used', + type: 'boolean', + }, + 'aws.cloudtrail.flattened.additional_eventdata': { + category: 'aws', + description: 'Additional data about the event that was not part of the request or response. ', + name: 'aws.cloudtrail.flattened.additional_eventdata', + type: 'flattened', + }, + 'aws.cloudtrail.flattened.request_parameters': { + category: 'aws', + description: 'The parameters, if any, that were sent with the request.', + name: 'aws.cloudtrail.flattened.request_parameters', + type: 'flattened', + }, + 'aws.cloudtrail.flattened.response_elements': { + category: 'aws', + description: + 'The response element for actions that make changes (create, update, or delete actions).', + name: 'aws.cloudtrail.flattened.response_elements', + type: 'flattened', + }, + 'aws.cloudtrail.flattened.service_event_details': { + category: 'aws', + description: 'Identifies the service event, including what triggered the event and the result.', + name: 'aws.cloudtrail.flattened.service_event_details', + type: 'flattened', + }, + 'aws.cloudwatch.message': { + category: 'aws', + description: 'CloudWatch log message. ', + name: 'aws.cloudwatch.message', + type: 'text', + }, + 'aws.ec2.ip_address': { + category: 'aws', + description: 'The internet address of the requester. ', + name: 'aws.ec2.ip_address', + type: 'keyword', + }, + 'aws.elb.name': { + category: 'aws', + description: 'The name of the load balancer. ', + name: 'aws.elb.name', + type: 'keyword', + }, + 'aws.elb.type': { + category: 'aws', + description: 'The type of the load balancer for v2 Load Balancers. ', + name: 'aws.elb.type', + type: 'keyword', + }, + 'aws.elb.target_group.arn': { + category: 'aws', + description: 'The ARN of the target group handling the request. ', + name: 'aws.elb.target_group.arn', + type: 'keyword', + }, + 'aws.elb.listener': { + category: 'aws', + description: 'The ELB listener that received the connection. ', + name: 'aws.elb.listener', + type: 'keyword', + }, + 'aws.elb.protocol': { + category: 'aws', + description: 'The protocol of the load balancer (http or tcp). ', + name: 'aws.elb.protocol', + type: 'keyword', + }, + 'aws.elb.request_processing_time.sec': { + category: 'aws', + description: + 'The total time in seconds since the connection or request is received until it is sent to a registered backend. ', + name: 'aws.elb.request_processing_time.sec', + type: 'float', + }, + 'aws.elb.backend_processing_time.sec': { + category: 'aws', + description: + 'The total time in seconds since the connection is sent to the backend till the backend starts responding. ', + name: 'aws.elb.backend_processing_time.sec', + type: 'float', + }, + 'aws.elb.response_processing_time.sec': { + category: 'aws', + description: + 'The total time in seconds since the response is received from the backend till it is sent to the client. ', + name: 'aws.elb.response_processing_time.sec', + type: 'float', + }, + 'aws.elb.connection_time.ms': { + category: 'aws', + description: + 'The total time of the connection in milliseconds, since it is opened till it is closed. ', + name: 'aws.elb.connection_time.ms', + type: 'long', + }, + 'aws.elb.tls_handshake_time.ms': { + category: 'aws', + description: + 'The total time for the TLS handshake to complete in milliseconds once the connection has been established. ', + name: 'aws.elb.tls_handshake_time.ms', + type: 'long', + }, + 'aws.elb.backend.ip': { + category: 'aws', + description: 'The IP address of the backend processing this connection. ', + name: 'aws.elb.backend.ip', + type: 'keyword', + }, + 'aws.elb.backend.port': { + category: 'aws', + description: 'The port in the backend processing this connection. ', + name: 'aws.elb.backend.port', + type: 'keyword', + }, + 'aws.elb.backend.http.response.status_code': { + category: 'aws', + description: + 'The status code from the backend (status code sent to the client from ELB is stored in `http.response.status_code` ', + name: 'aws.elb.backend.http.response.status_code', + type: 'keyword', + }, + 'aws.elb.ssl_cipher': { + category: 'aws', + description: 'The SSL cipher used in TLS/SSL connections. ', + name: 'aws.elb.ssl_cipher', + type: 'keyword', + }, + 'aws.elb.ssl_protocol': { + category: 'aws', + description: 'The SSL protocol used in TLS/SSL connections. ', + name: 'aws.elb.ssl_protocol', + type: 'keyword', + }, + 'aws.elb.chosen_cert.arn': { + category: 'aws', + description: + 'The ARN of the chosen certificate presented to the client in TLS/SSL connections. ', + name: 'aws.elb.chosen_cert.arn', + type: 'keyword', + }, + 'aws.elb.chosen_cert.serial': { + category: 'aws', + description: + 'The serial number of the chosen certificate presented to the client in TLS/SSL connections. ', + name: 'aws.elb.chosen_cert.serial', + type: 'keyword', + }, + 'aws.elb.incoming_tls_alert': { + category: 'aws', + description: + 'The integer value of TLS alerts received by the load balancer from the client, if present. ', + name: 'aws.elb.incoming_tls_alert', + type: 'keyword', + }, + 'aws.elb.tls_named_group': { + category: 'aws', + description: 'The TLS named group. ', + name: 'aws.elb.tls_named_group', + type: 'keyword', + }, + 'aws.elb.trace_id': { + category: 'aws', + description: 'The contents of the `X-Amzn-Trace-Id` header. ', + name: 'aws.elb.trace_id', + type: 'keyword', + }, + 'aws.elb.matched_rule_priority': { + category: 'aws', + description: 'The priority value of the rule that matched the request, if a rule matched. ', + name: 'aws.elb.matched_rule_priority', + type: 'keyword', + }, + 'aws.elb.action_executed': { + category: 'aws', + description: + 'The action executed when processing the request (forward, fixed-response, authenticate...). It can contain several values. ', + name: 'aws.elb.action_executed', + type: 'keyword', + }, + 'aws.elb.redirect_url': { + category: 'aws', + description: 'The URL used if a redirection action was executed. ', + name: 'aws.elb.redirect_url', + type: 'keyword', + }, + 'aws.elb.error.reason': { + category: 'aws', + description: 'The error reason if the executed action failed. ', + name: 'aws.elb.error.reason', + type: 'keyword', + }, + 'aws.s3access.bucket_owner': { + category: 'aws', + description: 'The canonical user ID of the owner of the source bucket. ', + name: 'aws.s3access.bucket_owner', + type: 'keyword', + }, + 'aws.s3access.bucket': { + category: 'aws', + description: 'The name of the bucket that the request was processed against. ', + name: 'aws.s3access.bucket', + type: 'keyword', + }, + 'aws.s3access.remote_ip': { + category: 'aws', + description: 'The apparent internet address of the requester. ', + name: 'aws.s3access.remote_ip', + type: 'ip', + }, + 'aws.s3access.requester': { + category: 'aws', + description: 'The canonical user ID of the requester, or a - for unauthenticated requests. ', + name: 'aws.s3access.requester', + type: 'keyword', + }, + 'aws.s3access.request_id': { + category: 'aws', + description: 'A string generated by Amazon S3 to uniquely identify each request. ', + name: 'aws.s3access.request_id', + type: 'keyword', + }, + 'aws.s3access.operation': { + category: 'aws', + description: + 'The operation listed here is declared as SOAP.operation, REST.HTTP_method.resource_type, WEBSITE.HTTP_method.resource_type, or BATCH.DELETE.OBJECT. ', + name: 'aws.s3access.operation', + type: 'keyword', + }, + 'aws.s3access.key': { + category: 'aws', + description: + 'The "key" part of the request, URL encoded, or "-" if the operation does not take a key parameter. ', + name: 'aws.s3access.key', + type: 'keyword', + }, + 'aws.s3access.request_uri': { + category: 'aws', + description: 'The Request-URI part of the HTTP request message. ', + name: 'aws.s3access.request_uri', + type: 'keyword', + }, + 'aws.s3access.http_status': { + category: 'aws', + description: 'The numeric HTTP status code of the response. ', + name: 'aws.s3access.http_status', + type: 'long', + }, + 'aws.s3access.error_code': { + category: 'aws', + description: 'The Amazon S3 Error Code, or "-" if no error occurred. ', + name: 'aws.s3access.error_code', + type: 'keyword', + }, + 'aws.s3access.bytes_sent': { + category: 'aws', + description: + 'The number of response bytes sent, excluding HTTP protocol overhead, or "-" if zero. ', + name: 'aws.s3access.bytes_sent', + type: 'long', + }, + 'aws.s3access.object_size': { + category: 'aws', + description: 'The total size of the object in question. ', + name: 'aws.s3access.object_size', + type: 'long', + }, + 'aws.s3access.total_time': { + category: 'aws', + description: + "The number of milliseconds the request was in flight from the server's perspective. ", + name: 'aws.s3access.total_time', + type: 'long', + }, + 'aws.s3access.turn_around_time': { + category: 'aws', + description: 'The number of milliseconds that Amazon S3 spent processing your request. ', + name: 'aws.s3access.turn_around_time', + type: 'long', + }, + 'aws.s3access.referrer': { + category: 'aws', + description: 'The value of the HTTP Referrer header, if present. ', + name: 'aws.s3access.referrer', + type: 'keyword', + }, + 'aws.s3access.user_agent': { + category: 'aws', + description: 'The value of the HTTP User-Agent header. ', + name: 'aws.s3access.user_agent', + type: 'keyword', + }, + 'aws.s3access.version_id': { + category: 'aws', + description: + 'The version ID in the request, or "-" if the operation does not take a versionId parameter. ', + name: 'aws.s3access.version_id', + type: 'keyword', + }, + 'aws.s3access.host_id': { + category: 'aws', + description: 'The x-amz-id-2 or Amazon S3 extended request ID. ', + name: 'aws.s3access.host_id', + type: 'keyword', + }, + 'aws.s3access.signature_version': { + category: 'aws', + description: + 'The signature version, SigV2 or SigV4, that was used to authenticate the request or a - for unauthenticated requests. ', + name: 'aws.s3access.signature_version', + type: 'keyword', + }, + 'aws.s3access.cipher_suite': { + category: 'aws', + description: + 'The Secure Sockets Layer (SSL) cipher that was negotiated for HTTPS request or a - for HTTP. ', + name: 'aws.s3access.cipher_suite', + type: 'keyword', + }, + 'aws.s3access.authentication_type': { + category: 'aws', + description: + 'The type of request authentication used, AuthHeader for authentication headers, QueryString for query string (pre-signed URL) or a - for unauthenticated requests. ', + name: 'aws.s3access.authentication_type', + type: 'keyword', + }, + 'aws.s3access.host_header': { + category: 'aws', + description: 'The endpoint used to connect to Amazon S3. ', + name: 'aws.s3access.host_header', + type: 'keyword', + }, + 'aws.s3access.tls_version': { + category: 'aws', + description: 'The Transport Layer Security (TLS) version negotiated by the client. ', + name: 'aws.s3access.tls_version', + type: 'keyword', + }, + 'aws.vpcflow.version': { + category: 'aws', + description: + 'The VPC Flow Logs version. If you use the default format, the version is 2. If you specify a custom format, the version is 3. ', + name: 'aws.vpcflow.version', + type: 'keyword', + }, + 'aws.vpcflow.account_id': { + category: 'aws', + description: 'The AWS account ID for the flow log. ', + name: 'aws.vpcflow.account_id', + type: 'keyword', + }, + 'aws.vpcflow.interface_id': { + category: 'aws', + description: 'The ID of the network interface for which the traffic is recorded. ', + name: 'aws.vpcflow.interface_id', + type: 'keyword', + }, + 'aws.vpcflow.action': { + category: 'aws', + description: 'The action that is associated with the traffic, ACCEPT or REJECT. ', + name: 'aws.vpcflow.action', + type: 'keyword', + }, + 'aws.vpcflow.log_status': { + category: 'aws', + description: 'The logging status of the flow log, OK, NODATA or SKIPDATA. ', + name: 'aws.vpcflow.log_status', + type: 'keyword', + }, + 'aws.vpcflow.instance_id': { + category: 'aws', + description: + "The ID of the instance that's associated with network interface for which the traffic is recorded, if the instance is owned by you. ", + name: 'aws.vpcflow.instance_id', + type: 'keyword', + }, + 'aws.vpcflow.pkt_srcaddr': { + category: 'aws', + description: 'The packet-level (original) source IP address of the traffic. ', + name: 'aws.vpcflow.pkt_srcaddr', + type: 'ip', + }, + 'aws.vpcflow.pkt_dstaddr': { + category: 'aws', + description: 'The packet-level (original) destination IP address for the traffic. ', + name: 'aws.vpcflow.pkt_dstaddr', + type: 'ip', + }, + 'aws.vpcflow.vpc_id': { + category: 'aws', + description: + 'The ID of the VPC that contains the network interface for which the traffic is recorded. ', + name: 'aws.vpcflow.vpc_id', + type: 'keyword', + }, + 'aws.vpcflow.subnet_id': { + category: 'aws', + description: + 'The ID of the subnet that contains the network interface for which the traffic is recorded. ', + name: 'aws.vpcflow.subnet_id', + type: 'keyword', + }, + 'aws.vpcflow.tcp_flags': { + category: 'aws', + description: 'The bitmask value for the following TCP flags: 2=SYN,18=SYN-ACK,1=FIN,4=RST ', + name: 'aws.vpcflow.tcp_flags', + type: 'keyword', + }, + 'aws.vpcflow.type': { + category: 'aws', + description: 'The type of traffic: IPv4, IPv6, or EFA. ', + name: 'aws.vpcflow.type', + type: 'keyword', + }, + 'azure.subscription_id': { + category: 'azure', + description: 'Azure subscription ID ', + name: 'azure.subscription_id', + type: 'keyword', + }, + 'azure.correlation_id': { + category: 'azure', + description: 'Correlation ID ', + name: 'azure.correlation_id', + type: 'keyword', + }, + 'azure.tenant_id': { + category: 'azure', + description: 'tenant ID ', + name: 'azure.tenant_id', + type: 'keyword', + }, + 'azure.resource.id': { + category: 'azure', + description: 'Resource ID ', + name: 'azure.resource.id', + type: 'keyword', + }, + 'azure.resource.group': { + category: 'azure', + description: 'Resource group ', + name: 'azure.resource.group', + type: 'keyword', + }, + 'azure.resource.provider': { + category: 'azure', + description: 'Resource type/namespace ', + name: 'azure.resource.provider', + type: 'keyword', + }, + 'azure.resource.namespace': { + category: 'azure', + description: 'Resource type/namespace ', + name: 'azure.resource.namespace', + type: 'keyword', + }, + 'azure.resource.name': { + category: 'azure', + description: 'Name ', + name: 'azure.resource.name', + type: 'keyword', + }, + 'azure.resource.authorization_rule': { + category: 'azure', + description: 'Authorization rule ', + name: 'azure.resource.authorization_rule', + type: 'keyword', + }, + 'azure.activitylogs.identity.claims_initiated_by_user.name': { + category: 'azure', + description: 'Name ', + name: 'azure.activitylogs.identity.claims_initiated_by_user.name', + type: 'keyword', + }, + 'azure.activitylogs.identity.claims_initiated_by_user.givenname': { + category: 'azure', + description: 'Givenname ', + name: 'azure.activitylogs.identity.claims_initiated_by_user.givenname', + type: 'keyword', + }, + 'azure.activitylogs.identity.claims_initiated_by_user.surname': { + category: 'azure', + description: 'Surname ', + name: 'azure.activitylogs.identity.claims_initiated_by_user.surname', + type: 'keyword', + }, + 'azure.activitylogs.identity.claims_initiated_by_user.fullname': { + category: 'azure', + description: 'Fullname ', + name: 'azure.activitylogs.identity.claims_initiated_by_user.fullname', + type: 'keyword', + }, + 'azure.activitylogs.identity.claims_initiated_by_user.schema': { + category: 'azure', + description: 'Schema ', + name: 'azure.activitylogs.identity.claims_initiated_by_user.schema', + type: 'keyword', + }, + 'azure.activitylogs.identity.claims.*': { + category: 'azure', + description: 'Claims ', + name: 'azure.activitylogs.identity.claims.*', + type: 'object', + }, + 'azure.activitylogs.identity.authorization.scope': { + category: 'azure', + description: 'Scope ', + name: 'azure.activitylogs.identity.authorization.scope', + type: 'keyword', + }, + 'azure.activitylogs.identity.authorization.action': { + category: 'azure', + description: 'Action ', + name: 'azure.activitylogs.identity.authorization.action', + type: 'keyword', + }, + 'azure.activitylogs.identity.authorization.evidence.role_assignment_scope': { + category: 'azure', + description: 'Role assignment scope ', + name: 'azure.activitylogs.identity.authorization.evidence.role_assignment_scope', + type: 'keyword', + }, + 'azure.activitylogs.identity.authorization.evidence.role_definition_id': { + category: 'azure', + description: 'Role definition ID ', + name: 'azure.activitylogs.identity.authorization.evidence.role_definition_id', + type: 'keyword', + }, + 'azure.activitylogs.identity.authorization.evidence.role': { + category: 'azure', + description: 'Role ', + name: 'azure.activitylogs.identity.authorization.evidence.role', + type: 'keyword', + }, + 'azure.activitylogs.identity.authorization.evidence.role_assignment_id': { + category: 'azure', + description: 'Role assignment ID ', + name: 'azure.activitylogs.identity.authorization.evidence.role_assignment_id', + type: 'keyword', + }, + 'azure.activitylogs.identity.authorization.evidence.principal_id': { + category: 'azure', + description: 'Principal ID ', + name: 'azure.activitylogs.identity.authorization.evidence.principal_id', + type: 'keyword', + }, + 'azure.activitylogs.identity.authorization.evidence.principal_type': { + category: 'azure', + description: 'Principal type ', + name: 'azure.activitylogs.identity.authorization.evidence.principal_type', + type: 'keyword', + }, + 'azure.activitylogs.operation_name': { + category: 'azure', + description: 'Operation name ', + name: 'azure.activitylogs.operation_name', + type: 'keyword', + }, + 'azure.activitylogs.result_type': { + category: 'azure', + description: 'Result type ', + name: 'azure.activitylogs.result_type', + type: 'keyword', + }, + 'azure.activitylogs.result_signature': { + category: 'azure', + description: 'Result signature ', + name: 'azure.activitylogs.result_signature', + type: 'keyword', + }, + 'azure.activitylogs.category': { + category: 'azure', + description: 'Category ', + name: 'azure.activitylogs.category', + type: 'keyword', + }, + 'azure.activitylogs.event_category': { + category: 'azure', + description: 'Event Category ', + name: 'azure.activitylogs.event_category', + type: 'keyword', + }, + 'azure.activitylogs.properties.service_request_id': { + category: 'azure', + description: 'Service Request Id ', + name: 'azure.activitylogs.properties.service_request_id', + type: 'keyword', + }, + 'azure.activitylogs.properties.status_code': { + category: 'azure', + description: 'Status code ', + name: 'azure.activitylogs.properties.status_code', + type: 'keyword', + }, + 'azure.auditlogs.category': { + category: 'azure', + description: 'The category of the operation. Currently, Audit is the only supported value. ', + name: 'azure.auditlogs.category', + type: 'keyword', + }, + 'azure.auditlogs.operation_name': { + category: 'azure', + description: 'The operation name ', + name: 'azure.auditlogs.operation_name', + type: 'keyword', + }, + 'azure.auditlogs.operation_version': { + category: 'azure', + description: 'The operation version ', + name: 'azure.auditlogs.operation_version', + type: 'keyword', + }, + 'azure.auditlogs.identity': { + category: 'azure', + description: 'Identity ', + name: 'azure.auditlogs.identity', + type: 'keyword', + }, + 'azure.auditlogs.tenant_id': { + category: 'azure', + description: 'Tenant ID ', + name: 'azure.auditlogs.tenant_id', + type: 'keyword', + }, + 'azure.auditlogs.result_signature': { + category: 'azure', + description: 'Result signature ', + name: 'azure.auditlogs.result_signature', + type: 'keyword', + }, + 'azure.auditlogs.properties.result': { + category: 'azure', + description: 'Log result ', + name: 'azure.auditlogs.properties.result', + type: 'keyword', + }, + 'azure.auditlogs.properties.activity_display_name': { + category: 'azure', + description: 'Activity display name ', + name: 'azure.auditlogs.properties.activity_display_name', + type: 'keyword', + }, + 'azure.auditlogs.properties.result_reason': { + category: 'azure', + description: 'Reason for the log result ', + name: 'azure.auditlogs.properties.result_reason', + type: 'keyword', + }, + 'azure.auditlogs.properties.correlation_id': { + category: 'azure', + description: 'Correlation ID ', + name: 'azure.auditlogs.properties.correlation_id', + type: 'keyword', + }, + 'azure.auditlogs.properties.logged_by_service': { + category: 'azure', + description: 'Logged by service ', + name: 'azure.auditlogs.properties.logged_by_service', + type: 'keyword', + }, + 'azure.auditlogs.properties.operation_type': { + category: 'azure', + description: 'Operation type ', + name: 'azure.auditlogs.properties.operation_type', + type: 'keyword', + }, + 'azure.auditlogs.properties.id': { + category: 'azure', + description: 'ID ', + name: 'azure.auditlogs.properties.id', + type: 'keyword', + }, + 'azure.auditlogs.properties.activity_datetime': { + category: 'azure', + description: 'Activity timestamp ', + name: 'azure.auditlogs.properties.activity_datetime', + type: 'date', + }, + 'azure.auditlogs.properties.category': { + category: 'azure', + description: 'category ', + name: 'azure.auditlogs.properties.category', + type: 'keyword', + }, + 'azure.auditlogs.properties.target_resources.*.display_name': { + category: 'azure', + description: 'Display name ', + name: 'azure.auditlogs.properties.target_resources.*.display_name', + type: 'keyword', + }, + 'azure.auditlogs.properties.target_resources.*.id': { + category: 'azure', + description: 'ID ', + name: 'azure.auditlogs.properties.target_resources.*.id', + type: 'keyword', + }, + 'azure.auditlogs.properties.target_resources.*.type': { + category: 'azure', + description: 'Type ', + name: 'azure.auditlogs.properties.target_resources.*.type', + type: 'keyword', + }, + 'azure.auditlogs.properties.target_resources.*.ip_address': { + category: 'azure', + description: 'ip Address ', + name: 'azure.auditlogs.properties.target_resources.*.ip_address', + type: 'keyword', + }, + 'azure.auditlogs.properties.target_resources.*.user_principal_name': { + category: 'azure', + description: 'User principal name ', + name: 'azure.auditlogs.properties.target_resources.*.user_principal_name', + type: 'keyword', + }, + 'azure.auditlogs.properties.target_resources.*.modified_properties.*.new_value': { + category: 'azure', + description: 'New value ', + name: 'azure.auditlogs.properties.target_resources.*.modified_properties.*.new_value', + type: 'keyword', + }, + 'azure.auditlogs.properties.target_resources.*.modified_properties.*.display_name': { + category: 'azure', + description: 'Display value ', + name: 'azure.auditlogs.properties.target_resources.*.modified_properties.*.display_name', + type: 'keyword', + }, + 'azure.auditlogs.properties.target_resources.*.modified_properties.*.old_value': { + category: 'azure', + description: 'Old value ', + name: 'azure.auditlogs.properties.target_resources.*.modified_properties.*.old_value', + type: 'keyword', + }, + 'azure.auditlogs.properties.initiated_by.app.servicePrincipalName': { + category: 'azure', + description: 'Service principal name ', + name: 'azure.auditlogs.properties.initiated_by.app.servicePrincipalName', + type: 'keyword', + }, + 'azure.auditlogs.properties.initiated_by.app.displayName': { + category: 'azure', + description: 'Display name ', + name: 'azure.auditlogs.properties.initiated_by.app.displayName', + type: 'keyword', + }, + 'azure.auditlogs.properties.initiated_by.app.appId': { + category: 'azure', + description: 'App ID ', + name: 'azure.auditlogs.properties.initiated_by.app.appId', + type: 'keyword', + }, + 'azure.auditlogs.properties.initiated_by.app.servicePrincipalId': { + category: 'azure', + description: 'Service principal ID ', + name: 'azure.auditlogs.properties.initiated_by.app.servicePrincipalId', + type: 'keyword', + }, + 'azure.auditlogs.properties.initiated_by.user.userPrincipalName': { + category: 'azure', + description: 'User principal name ', + name: 'azure.auditlogs.properties.initiated_by.user.userPrincipalName', + type: 'keyword', + }, + 'azure.auditlogs.properties.initiated_by.user.displayName': { + category: 'azure', + description: 'Display name ', + name: 'azure.auditlogs.properties.initiated_by.user.displayName', + type: 'keyword', + }, + 'azure.auditlogs.properties.initiated_by.user.id': { + category: 'azure', + description: 'ID ', + name: 'azure.auditlogs.properties.initiated_by.user.id', + type: 'keyword', + }, + 'azure.auditlogs.properties.initiated_by.user.ipAddress': { + category: 'azure', + description: 'ip Address ', + name: 'azure.auditlogs.properties.initiated_by.user.ipAddress', + type: 'keyword', + }, + 'azure.signinlogs.operation_name': { + category: 'azure', + description: 'The operation name ', + name: 'azure.signinlogs.operation_name', + type: 'keyword', + }, + 'azure.signinlogs.operation_version': { + category: 'azure', + description: 'The operation version ', + name: 'azure.signinlogs.operation_version', + type: 'keyword', + }, + 'azure.signinlogs.tenant_id': { + category: 'azure', + description: 'Tenant ID ', + name: 'azure.signinlogs.tenant_id', + type: 'keyword', + }, + 'azure.signinlogs.result_signature': { + category: 'azure', + description: 'Result signature ', + name: 'azure.signinlogs.result_signature', + type: 'keyword', + }, + 'azure.signinlogs.result_description': { + category: 'azure', + description: 'Result description ', + name: 'azure.signinlogs.result_description', + type: 'keyword', + }, + 'azure.signinlogs.result_type': { + category: 'azure', + description: 'Result type ', + name: 'azure.signinlogs.result_type', + type: 'keyword', + }, + 'azure.signinlogs.identity': { + category: 'azure', + description: 'Identity ', + name: 'azure.signinlogs.identity', + type: 'keyword', + }, + 'azure.signinlogs.category': { + category: 'azure', + description: 'Category ', + name: 'azure.signinlogs.category', + type: 'keyword', + }, + 'azure.signinlogs.properties.id': { + category: 'azure', + description: 'ID ', + name: 'azure.signinlogs.properties.id', + type: 'keyword', + }, + 'azure.signinlogs.properties.created_at': { + category: 'azure', + description: 'Created date time ', + name: 'azure.signinlogs.properties.created_at', + type: 'date', + }, + 'azure.signinlogs.properties.user_display_name': { + category: 'azure', + description: 'User display name ', + name: 'azure.signinlogs.properties.user_display_name', + type: 'keyword', + }, + 'azure.signinlogs.properties.correlation_id': { + category: 'azure', + description: 'Correlation ID ', + name: 'azure.signinlogs.properties.correlation_id', + type: 'keyword', + }, + 'azure.signinlogs.properties.user_principal_name': { + category: 'azure', + description: 'User principal name ', + name: 'azure.signinlogs.properties.user_principal_name', + type: 'keyword', + }, + 'azure.signinlogs.properties.user_id': { + category: 'azure', + description: 'User ID ', + name: 'azure.signinlogs.properties.user_id', + type: 'keyword', + }, + 'azure.signinlogs.properties.app_id': { + category: 'azure', + description: 'App ID ', + name: 'azure.signinlogs.properties.app_id', + type: 'keyword', + }, + 'azure.signinlogs.properties.app_display_name': { + category: 'azure', + description: 'App display name ', + name: 'azure.signinlogs.properties.app_display_name', + type: 'keyword', + }, + 'azure.signinlogs.properties.ip_address': { + category: 'azure', + description: 'Ip address ', + name: 'azure.signinlogs.properties.ip_address', + type: 'keyword', + }, + 'azure.signinlogs.properties.client_app_used': { + category: 'azure', + description: 'Client app used ', + name: 'azure.signinlogs.properties.client_app_used', + type: 'keyword', + }, + 'azure.signinlogs.properties.conditional_access_status': { + category: 'azure', + description: 'Conditional access status ', + name: 'azure.signinlogs.properties.conditional_access_status', + type: 'keyword', + }, + 'azure.signinlogs.properties.original_request_id': { + category: 'azure', + description: 'Original request ID ', + name: 'azure.signinlogs.properties.original_request_id', + type: 'keyword', + }, + 'azure.signinlogs.properties.is_interactive': { + category: 'azure', + description: 'Is interactive ', + name: 'azure.signinlogs.properties.is_interactive', + type: 'keyword', + }, + 'azure.signinlogs.properties.token_issuer_name': { + category: 'azure', + description: 'Token issuer name ', + name: 'azure.signinlogs.properties.token_issuer_name', + type: 'keyword', + }, + 'azure.signinlogs.properties.token_issuer_type': { + category: 'azure', + description: 'Token issuer type ', + name: 'azure.signinlogs.properties.token_issuer_type', + type: 'keyword', + }, + 'azure.signinlogs.properties.processing_time_ms': { + category: 'azure', + description: 'Processing time in milliseconds ', + name: 'azure.signinlogs.properties.processing_time_ms', + type: 'float', + }, + 'azure.signinlogs.properties.risk_detail': { + category: 'azure', + description: 'Risk detail ', + name: 'azure.signinlogs.properties.risk_detail', + type: 'keyword', + }, + 'azure.signinlogs.properties.risk_level_aggregated': { + category: 'azure', + description: 'Risk level aggregated ', + name: 'azure.signinlogs.properties.risk_level_aggregated', + type: 'keyword', + }, + 'azure.signinlogs.properties.risk_level_during_signin': { + category: 'azure', + description: 'Risk level during signIn ', + name: 'azure.signinlogs.properties.risk_level_during_signin', + type: 'keyword', + }, + 'azure.signinlogs.properties.risk_state': { + category: 'azure', + description: 'Risk state ', + name: 'azure.signinlogs.properties.risk_state', + type: 'keyword', + }, + 'azure.signinlogs.properties.resource_display_name': { + category: 'azure', + description: 'Resource display name ', + name: 'azure.signinlogs.properties.resource_display_name', + type: 'keyword', + }, + 'azure.signinlogs.properties.status.error_code': { + category: 'azure', + description: 'Error code ', + name: 'azure.signinlogs.properties.status.error_code', + type: 'keyword', + }, + 'azure.signinlogs.properties.device_detail.device_id': { + category: 'azure', + description: 'Device ID ', + name: 'azure.signinlogs.properties.device_detail.device_id', + type: 'keyword', + }, + 'azure.signinlogs.properties.device_detail.operating_system': { + category: 'azure', + description: 'Operating system ', + name: 'azure.signinlogs.properties.device_detail.operating_system', + type: 'keyword', + }, + 'azure.signinlogs.properties.device_detail.browser': { + category: 'azure', + description: 'Browser ', + name: 'azure.signinlogs.properties.device_detail.browser', + type: 'keyword', + }, + 'azure.signinlogs.properties.device_detail.display_name': { + category: 'azure', + description: 'Display name ', + name: 'azure.signinlogs.properties.device_detail.display_name', + type: 'keyword', + }, + 'azure.signinlogs.properties.device_detail.trust_type': { + category: 'azure', + description: 'Trust type ', + name: 'azure.signinlogs.properties.device_detail.trust_type', + type: 'keyword', + }, + 'azure.signinlogs.properties.service_principal_id': { + category: 'azure', + description: 'Status ', + name: 'azure.signinlogs.properties.service_principal_id', + type: 'keyword', + }, + 'network.interface.name': { + category: 'network', + description: 'Name of the network interface where the traffic has been observed. ', + name: 'network.interface.name', + type: 'keyword', + }, + 'rsa.internal.msg': { + category: 'rsa', + description: 'This key is used to capture the raw message that comes into the Log Decoder', + name: 'rsa.internal.msg', + type: 'keyword', + }, + 'rsa.internal.messageid': { + category: 'rsa', + name: 'rsa.internal.messageid', + type: 'keyword', + }, + 'rsa.internal.event_desc': { + category: 'rsa', + name: 'rsa.internal.event_desc', + type: 'keyword', + }, + 'rsa.internal.message': { + category: 'rsa', + description: 'This key captures the contents of instant messages', + name: 'rsa.internal.message', + type: 'keyword', + }, + 'rsa.internal.time': { + category: 'rsa', + description: + 'This is the time at which a session hits a NetWitness Decoder. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness.', + name: 'rsa.internal.time', + type: 'date', + }, + 'rsa.internal.level': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.level', + type: 'long', + }, + 'rsa.internal.msg_id': { + category: 'rsa', + description: + 'This is the Message ID1 value that identifies the exact log parser definition which parses a particular log session. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.msg_id', + type: 'keyword', + }, + 'rsa.internal.msg_vid': { + category: 'rsa', + description: + 'This is the Message ID2 value that identifies the exact log parser definition which parses a particular log session. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.msg_vid', + type: 'keyword', + }, + 'rsa.internal.data': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.data', + type: 'keyword', + }, + 'rsa.internal.obj_server': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.obj_server', + type: 'keyword', + }, + 'rsa.internal.obj_val': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.obj_val', + type: 'keyword', + }, + 'rsa.internal.resource': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.resource', + type: 'keyword', + }, + 'rsa.internal.obj_id': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.obj_id', + type: 'keyword', + }, + 'rsa.internal.statement': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.statement', + type: 'keyword', + }, + 'rsa.internal.audit_class': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.audit_class', + type: 'keyword', + }, + 'rsa.internal.entry': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.entry', + type: 'keyword', + }, + 'rsa.internal.hcode': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.hcode', + type: 'keyword', + }, + 'rsa.internal.inode': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.inode', + type: 'long', + }, + 'rsa.internal.resource_class': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.resource_class', + type: 'keyword', + }, + 'rsa.internal.dead': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.dead', + type: 'long', + }, + 'rsa.internal.feed_desc': { + category: 'rsa', + description: + 'This is used to capture the description of the feed. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.feed_desc', + type: 'keyword', + }, + 'rsa.internal.feed_name': { + category: 'rsa', + description: + 'This is used to capture the name of the feed. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.feed_name', + type: 'keyword', + }, + 'rsa.internal.cid': { + category: 'rsa', + description: + 'This is the unique identifier used to identify a NetWitness Concentrator. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.cid', + type: 'keyword', + }, + 'rsa.internal.device_class': { + category: 'rsa', + description: + 'This is the Classification of the Log Event Source under a predefined fixed set of Event Source Classifications. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.device_class', + type: 'keyword', + }, + 'rsa.internal.device_group': { + category: 'rsa', + description: + 'This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.device_group', + type: 'keyword', + }, + 'rsa.internal.device_host': { + category: 'rsa', + description: + 'This is the Hostname of the log Event Source sending the logs to NetWitness. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.device_host', + type: 'keyword', + }, + 'rsa.internal.device_ip': { + category: 'rsa', + description: + 'This is the IPv4 address of the Log Event Source sending the logs to NetWitness. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.device_ip', + type: 'ip', + }, + 'rsa.internal.device_ipv6': { + category: 'rsa', + description: + 'This is the IPv6 address of the Log Event Source sending the logs to NetWitness. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.device_ipv6', + type: 'ip', + }, + 'rsa.internal.device_type': { + category: 'rsa', + description: + 'This is the name of the log parser which parsed a given session. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.device_type', + type: 'keyword', + }, + 'rsa.internal.device_type_id': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.device_type_id', + type: 'long', + }, + 'rsa.internal.did': { + category: 'rsa', + description: + 'This is the unique identifier used to identify a NetWitness Decoder. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.did', + type: 'keyword', + }, + 'rsa.internal.entropy_req': { + category: 'rsa', + description: + 'This key is only used by the Entropy Parser, the Meta Type can be either UInt16 or Float32 based on the configuration', + name: 'rsa.internal.entropy_req', + type: 'long', + }, + 'rsa.internal.entropy_res': { + category: 'rsa', + description: + 'This key is only used by the Entropy Parser, the Meta Type can be either UInt16 or Float32 based on the configuration', + name: 'rsa.internal.entropy_res', + type: 'long', + }, + 'rsa.internal.event_name': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.event_name', + type: 'keyword', + }, + 'rsa.internal.feed_category': { + category: 'rsa', + description: + 'This is used to capture the category of the feed. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.feed_category', + type: 'keyword', + }, + 'rsa.internal.forward_ip': { + category: 'rsa', + description: + 'This key should be used to capture the IPV4 address of a relay system which forwarded the events from the original system to NetWitness.', + name: 'rsa.internal.forward_ip', + type: 'ip', + }, + 'rsa.internal.forward_ipv6': { + category: 'rsa', + description: + 'This key is used to capture the IPV6 address of a relay system which forwarded the events from the original system to NetWitness. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.forward_ipv6', + type: 'ip', + }, + 'rsa.internal.header_id': { + category: 'rsa', + description: + 'This is the Header ID value that identifies the exact log parser header definition that parses a particular log session. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.header_id', + type: 'keyword', + }, + 'rsa.internal.lc_cid': { + category: 'rsa', + description: + 'This is a unique Identifier of a Log Collector. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.lc_cid', + type: 'keyword', + }, + 'rsa.internal.lc_ctime': { + category: 'rsa', + description: + 'This is the time at which a log is collected in a NetWitness Log Collector. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.lc_ctime', + type: 'date', + }, + 'rsa.internal.mcb_req': { + category: 'rsa', + description: + 'This key is only used by the Entropy Parser, the most common byte request is simply which byte for each side (0 thru 255) was seen the most', + name: 'rsa.internal.mcb_req', + type: 'long', + }, + 'rsa.internal.mcb_res': { + category: 'rsa', + description: + 'This key is only used by the Entropy Parser, the most common byte response is simply which byte for each side (0 thru 255) was seen the most', + name: 'rsa.internal.mcb_res', + type: 'long', + }, + 'rsa.internal.mcbc_req': { + category: 'rsa', + description: + 'This key is only used by the Entropy Parser, the most common byte count is the number of times the most common byte (above) was seen in the session streams', + name: 'rsa.internal.mcbc_req', + type: 'long', + }, + 'rsa.internal.mcbc_res': { + category: 'rsa', + description: + 'This key is only used by the Entropy Parser, the most common byte count is the number of times the most common byte (above) was seen in the session streams', + name: 'rsa.internal.mcbc_res', + type: 'long', + }, + 'rsa.internal.medium': { + category: 'rsa', + description: + 'This key is used to identify if it’s a log/packet session or Layer 2 Encapsulation Type. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness. 32 = log, 33 = correlation session, < 32 is packet session', + name: 'rsa.internal.medium', + type: 'long', + }, + 'rsa.internal.node_name': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.node_name', + type: 'keyword', + }, + 'rsa.internal.nwe_callback_id': { + category: 'rsa', + description: 'This key denotes that event is endpoint related', + name: 'rsa.internal.nwe_callback_id', + type: 'keyword', + }, + 'rsa.internal.parse_error': { + category: 'rsa', + description: + 'This is a special key that stores any Meta key validation error found while parsing a log session. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.parse_error', + type: 'keyword', + }, + 'rsa.internal.payload_req': { + category: 'rsa', + description: + 'This key is only used by the Entropy Parser, the payload size metrics are the payload sizes of each session side at the time of parsing. However, in order to keep', + name: 'rsa.internal.payload_req', + type: 'long', + }, + 'rsa.internal.payload_res': { + category: 'rsa', + description: + 'This key is only used by the Entropy Parser, the payload size metrics are the payload sizes of each session side at the time of parsing. However, in order to keep', + name: 'rsa.internal.payload_res', + type: 'long', + }, + 'rsa.internal.process_vid_dst': { + category: 'rsa', + description: + 'Endpoint generates and uses a unique virtual ID to identify any similar group of process. This ID represents the target process.', + name: 'rsa.internal.process_vid_dst', + type: 'keyword', + }, + 'rsa.internal.process_vid_src': { + category: 'rsa', + description: + 'Endpoint generates and uses a unique virtual ID to identify any similar group of process. This ID represents the source process.', + name: 'rsa.internal.process_vid_src', + type: 'keyword', + }, + 'rsa.internal.rid': { + category: 'rsa', + description: + 'This is a special ID of the Remote Session created by NetWitness Decoder. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.rid', + type: 'long', + }, + 'rsa.internal.session_split': { + category: 'rsa', + description: + 'This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.session_split', + type: 'keyword', + }, + 'rsa.internal.site': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.internal.site', + type: 'keyword', + }, + 'rsa.internal.size': { + category: 'rsa', + description: + 'This is the size of the session as seen by the NetWitness Decoder. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.size', + type: 'long', + }, + 'rsa.internal.sourcefile': { + category: 'rsa', + description: + 'This is the name of the log file or PCAPs that can be imported into NetWitness. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.internal.sourcefile', + type: 'keyword', + }, + 'rsa.internal.ubc_req': { + category: 'rsa', + description: + 'This key is only used by the Entropy Parser, Unique byte count is the number of unique bytes seen in each stream. 256 would mean all byte values of 0 thru 255 were seen at least once', + name: 'rsa.internal.ubc_req', + type: 'long', + }, + 'rsa.internal.ubc_res': { + category: 'rsa', + description: + 'This key is only used by the Entropy Parser, Unique byte count is the number of unique bytes seen in each stream. 256 would mean all byte values of 0 thru 255 were seen at least once', + name: 'rsa.internal.ubc_res', + type: 'long', + }, + 'rsa.internal.word': { + category: 'rsa', + description: + 'This is used by the Word Parsing technology to capture the first 5 character of every word in an unparsed log', + name: 'rsa.internal.word', + type: 'keyword', + }, + 'rsa.time.event_time': { + category: 'rsa', + description: + 'This key is used to capture the time mentioned in a raw session that represents the actual time an event occured in a standard normalized form', + name: 'rsa.time.event_time', + type: 'date', + }, + 'rsa.time.duration_time': { + category: 'rsa', + description: 'This key is used to capture the normalized duration/lifetime in seconds.', + name: 'rsa.time.duration_time', + type: 'double', + }, + 'rsa.time.event_time_str': { + category: 'rsa', + description: + 'This key is used to capture the incomplete time mentioned in a session as a string', + name: 'rsa.time.event_time_str', + type: 'keyword', + }, + 'rsa.time.starttime': { + category: 'rsa', + description: + 'This key is used to capture the Start time mentioned in a session in a standard form', + name: 'rsa.time.starttime', + type: 'date', + }, + 'rsa.time.month': { + category: 'rsa', + name: 'rsa.time.month', + type: 'keyword', + }, + 'rsa.time.day': { + category: 'rsa', + name: 'rsa.time.day', + type: 'keyword', + }, + 'rsa.time.endtime': { + category: 'rsa', + description: + 'This key is used to capture the End time mentioned in a session in a standard form', + name: 'rsa.time.endtime', + type: 'date', + }, + 'rsa.time.timezone': { + category: 'rsa', + description: 'This key is used to capture the timezone of the Event Time', + name: 'rsa.time.timezone', + type: 'keyword', + }, + 'rsa.time.duration_str': { + category: 'rsa', + description: 'A text string version of the duration', + name: 'rsa.time.duration_str', + type: 'keyword', + }, + 'rsa.time.date': { + category: 'rsa', + name: 'rsa.time.date', + type: 'keyword', + }, + 'rsa.time.year': { + category: 'rsa', + name: 'rsa.time.year', + type: 'keyword', + }, + 'rsa.time.recorded_time': { + category: 'rsa', + description: + "The event time as recorded by the system the event is collected from. The usage scenario is a multi-tier application where the management layer of the system records it's own timestamp at the time of collection from its child nodes. Must be in timestamp format.", + name: 'rsa.time.recorded_time', + type: 'date', + }, + 'rsa.time.datetime': { + category: 'rsa', + name: 'rsa.time.datetime', + type: 'keyword', + }, + 'rsa.time.effective_time': { + category: 'rsa', + description: + 'This key is the effective time referenced by an individual event in a Standard Timestamp format', + name: 'rsa.time.effective_time', + type: 'date', + }, + 'rsa.time.expire_time': { + category: 'rsa', + description: 'This key is the timestamp that explicitly refers to an expiration.', + name: 'rsa.time.expire_time', + type: 'date', + }, + 'rsa.time.process_time': { + category: 'rsa', + description: 'Deprecated, use duration.time', + name: 'rsa.time.process_time', + type: 'keyword', + }, + 'rsa.time.hour': { + category: 'rsa', + name: 'rsa.time.hour', + type: 'keyword', + }, + 'rsa.time.min': { + category: 'rsa', + name: 'rsa.time.min', + type: 'keyword', + }, + 'rsa.time.timestamp': { + category: 'rsa', + name: 'rsa.time.timestamp', + type: 'keyword', + }, + 'rsa.time.event_queue_time': { + category: 'rsa', + description: 'This key is the Time that the event was queued.', + name: 'rsa.time.event_queue_time', + type: 'date', + }, + 'rsa.time.p_time1': { + category: 'rsa', + name: 'rsa.time.p_time1', + type: 'keyword', + }, + 'rsa.time.tzone': { + category: 'rsa', + name: 'rsa.time.tzone', + type: 'keyword', + }, + 'rsa.time.eventtime': { + category: 'rsa', + name: 'rsa.time.eventtime', + type: 'keyword', + }, + 'rsa.time.gmtdate': { + category: 'rsa', + name: 'rsa.time.gmtdate', + type: 'keyword', + }, + 'rsa.time.gmttime': { + category: 'rsa', + name: 'rsa.time.gmttime', + type: 'keyword', + }, + 'rsa.time.p_date': { + category: 'rsa', + name: 'rsa.time.p_date', + type: 'keyword', + }, + 'rsa.time.p_month': { + category: 'rsa', + name: 'rsa.time.p_month', + type: 'keyword', + }, + 'rsa.time.p_time': { + category: 'rsa', + name: 'rsa.time.p_time', + type: 'keyword', + }, + 'rsa.time.p_time2': { + category: 'rsa', + name: 'rsa.time.p_time2', + type: 'keyword', + }, + 'rsa.time.p_year': { + category: 'rsa', + name: 'rsa.time.p_year', + type: 'keyword', + }, + 'rsa.time.expire_time_str': { + category: 'rsa', + description: + 'This key is used to capture incomplete timestamp that explicitly refers to an expiration.', + name: 'rsa.time.expire_time_str', + type: 'keyword', + }, + 'rsa.time.stamp': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.time.stamp', + type: 'date', + }, + 'rsa.misc.action': { + category: 'rsa', + name: 'rsa.misc.action', + type: 'keyword', + }, + 'rsa.misc.result': { + category: 'rsa', + description: + 'This key is used to capture the outcome/result string value of an action in a session.', + name: 'rsa.misc.result', + type: 'keyword', + }, + 'rsa.misc.severity': { + category: 'rsa', + description: 'This key is used to capture the severity given the session', + name: 'rsa.misc.severity', + type: 'keyword', + }, + 'rsa.misc.event_type': { + category: 'rsa', + description: 'This key captures the event category type as specified by the event source.', + name: 'rsa.misc.event_type', + type: 'keyword', + }, + 'rsa.misc.reference_id': { + category: 'rsa', + description: 'This key is used to capture an event id from the session directly', + name: 'rsa.misc.reference_id', + type: 'keyword', + }, + 'rsa.misc.version': { + category: 'rsa', + description: + 'This key captures Version of the application or OS which is generating the event.', + name: 'rsa.misc.version', + type: 'keyword', + }, + 'rsa.misc.disposition': { + category: 'rsa', + description: 'This key captures the The end state of an action.', + name: 'rsa.misc.disposition', + type: 'keyword', + }, + 'rsa.misc.result_code': { + category: 'rsa', + description: + 'This key is used to capture the outcome/result numeric value of an action in a session', + name: 'rsa.misc.result_code', + type: 'keyword', + }, + 'rsa.misc.category': { + category: 'rsa', + description: + 'This key is used to capture the category of an event given by the vendor in the session', + name: 'rsa.misc.category', + type: 'keyword', + }, + 'rsa.misc.obj_name': { + category: 'rsa', + description: 'This is used to capture name of object', + name: 'rsa.misc.obj_name', + type: 'keyword', + }, + 'rsa.misc.obj_type': { + category: 'rsa', + description: 'This is used to capture type of object', + name: 'rsa.misc.obj_type', + type: 'keyword', + }, + 'rsa.misc.event_source': { + category: 'rsa', + description: 'This key captures Source of the event that’s not a hostname', + name: 'rsa.misc.event_source', + type: 'keyword', + }, + 'rsa.misc.log_session_id': { + category: 'rsa', + description: 'This key is used to capture a sessionid from the session directly', + name: 'rsa.misc.log_session_id', + type: 'keyword', + }, + 'rsa.misc.group': { + category: 'rsa', + description: 'This key captures the Group Name value', + name: 'rsa.misc.group', + type: 'keyword', + }, + 'rsa.misc.policy_name': { + category: 'rsa', + description: 'This key is used to capture the Policy Name only.', + name: 'rsa.misc.policy_name', + type: 'keyword', + }, + 'rsa.misc.rule_name': { + category: 'rsa', + description: 'This key captures the Rule Name', + name: 'rsa.misc.rule_name', + type: 'keyword', + }, + 'rsa.misc.context': { + category: 'rsa', + description: 'This key captures Information which adds additional context to the event.', + name: 'rsa.misc.context', + type: 'keyword', + }, + 'rsa.misc.change_new': { + category: 'rsa', + description: + 'This key is used to capture the new values of the attribute that’s changing in a session', + name: 'rsa.misc.change_new', + type: 'keyword', + }, + 'rsa.misc.space': { + category: 'rsa', + name: 'rsa.misc.space', + type: 'keyword', + }, + 'rsa.misc.client': { + category: 'rsa', + description: + 'This key is used to capture only the name of the client application requesting resources of the server. See the user.agent meta key for capture of the specific user agent identifier or browser identification string.', + name: 'rsa.misc.client', + type: 'keyword', + }, + 'rsa.misc.msgIdPart1': { + category: 'rsa', + name: 'rsa.misc.msgIdPart1', + type: 'keyword', + }, + 'rsa.misc.msgIdPart2': { + category: 'rsa', + name: 'rsa.misc.msgIdPart2', + type: 'keyword', + }, + 'rsa.misc.change_old': { + category: 'rsa', + description: + 'This key is used to capture the old value of the attribute that’s changing in a session', + name: 'rsa.misc.change_old', + type: 'keyword', + }, + 'rsa.misc.operation_id': { + category: 'rsa', + description: + 'An alert number or operation number. The values should be unique and non-repeating.', + name: 'rsa.misc.operation_id', + type: 'keyword', + }, + 'rsa.misc.event_state': { + category: 'rsa', + description: + 'This key captures the current state of the object/item referenced within the event. Describing an on-going event.', + name: 'rsa.misc.event_state', + type: 'keyword', + }, + 'rsa.misc.group_object': { + category: 'rsa', + description: 'This key captures a collection/grouping of entities. Specific usage', + name: 'rsa.misc.group_object', + type: 'keyword', + }, + 'rsa.misc.node': { + category: 'rsa', + description: + 'Common use case is the node name within a cluster. The cluster name is reflected by the host name.', + name: 'rsa.misc.node', + type: 'keyword', + }, + 'rsa.misc.rule': { + category: 'rsa', + description: 'This key captures the Rule number', + name: 'rsa.misc.rule', + type: 'keyword', + }, + 'rsa.misc.device_name': { + category: 'rsa', + description: + 'This is used to capture name of the Device associated with the node Like: a physical disk, printer, etc', + name: 'rsa.misc.device_name', + type: 'keyword', + }, + 'rsa.misc.param': { + category: 'rsa', + description: 'This key is the parameters passed as part of a command or application, etc.', + name: 'rsa.misc.param', + type: 'keyword', + }, + 'rsa.misc.change_attrib': { + category: 'rsa', + description: + 'This key is used to capture the name of the attribute that’s changing in a session', + name: 'rsa.misc.change_attrib', + type: 'keyword', + }, + 'rsa.misc.event_computer': { + category: 'rsa', + description: + 'This key is a windows only concept, where this key is used to capture fully qualified domain name in a windows log.', + name: 'rsa.misc.event_computer', + type: 'keyword', + }, + 'rsa.misc.reference_id1': { + category: 'rsa', + description: 'This key is for Linked ID to be used as an addition to "reference.id"', + name: 'rsa.misc.reference_id1', + type: 'keyword', + }, + 'rsa.misc.event_log': { + category: 'rsa', + description: 'This key captures the Name of the event log', + name: 'rsa.misc.event_log', + type: 'keyword', + }, + 'rsa.misc.OS': { + category: 'rsa', + description: 'This key captures the Name of the Operating System', + name: 'rsa.misc.OS', + type: 'keyword', + }, + 'rsa.misc.terminal': { + category: 'rsa', + description: 'This key captures the Terminal Names only', + name: 'rsa.misc.terminal', + type: 'keyword', + }, + 'rsa.misc.msgIdPart3': { + category: 'rsa', + name: 'rsa.misc.msgIdPart3', + type: 'keyword', + }, + 'rsa.misc.filter': { + category: 'rsa', + description: 'This key captures Filter used to reduce result set', + name: 'rsa.misc.filter', + type: 'keyword', + }, + 'rsa.misc.serial_number': { + category: 'rsa', + description: 'This key is the Serial number associated with a physical asset.', + name: 'rsa.misc.serial_number', + type: 'keyword', + }, + 'rsa.misc.checksum': { + category: 'rsa', + description: + 'This key is used to capture the checksum or hash of the entity such as a file or process. Checksum should be used over checksum.src or checksum.dst when it is unclear whether the entity is a source or target of an action.', + name: 'rsa.misc.checksum', + type: 'keyword', + }, + 'rsa.misc.event_user': { + category: 'rsa', + description: + 'This key is a windows only concept, where this key is used to capture combination of domain name and username in a windows log.', + name: 'rsa.misc.event_user', + type: 'keyword', + }, + 'rsa.misc.virusname': { + category: 'rsa', + description: 'This key captures the name of the virus', + name: 'rsa.misc.virusname', + type: 'keyword', + }, + 'rsa.misc.content_type': { + category: 'rsa', + description: 'This key is used to capture Content Type only.', + name: 'rsa.misc.content_type', + type: 'keyword', + }, + 'rsa.misc.group_id': { + category: 'rsa', + description: 'This key captures Group ID Number (related to the group name)', + name: 'rsa.misc.group_id', + type: 'keyword', + }, + 'rsa.misc.policy_id': { + category: 'rsa', + description: + 'This key is used to capture the Policy ID only, this should be a numeric value, use policy.name otherwise', + name: 'rsa.misc.policy_id', + type: 'keyword', + }, + 'rsa.misc.vsys': { + category: 'rsa', + description: 'This key captures Virtual System Name', + name: 'rsa.misc.vsys', + type: 'keyword', + }, + 'rsa.misc.connection_id': { + category: 'rsa', + description: 'This key captures the Connection ID', + name: 'rsa.misc.connection_id', + type: 'keyword', + }, + 'rsa.misc.reference_id2': { + category: 'rsa', + description: + 'This key is for the 2nd Linked ID. Can be either linked to "reference.id" or "reference.id1" value but should not be used unless the other two variables are in play.', + name: 'rsa.misc.reference_id2', + type: 'keyword', + }, + 'rsa.misc.sensor': { + category: 'rsa', + description: 'This key captures Name of the sensor. Typically used in IDS/IPS based devices', + name: 'rsa.misc.sensor', + type: 'keyword', + }, + 'rsa.misc.sig_id': { + category: 'rsa', + description: 'This key captures IDS/IPS Int Signature ID', + name: 'rsa.misc.sig_id', + type: 'long', + }, + 'rsa.misc.port_name': { + category: 'rsa', + description: + 'This key is used for Physical or logical port connection but does NOT include a network port. (Example: Printer port name).', + name: 'rsa.misc.port_name', + type: 'keyword', + }, + 'rsa.misc.rule_group': { + category: 'rsa', + description: 'This key captures the Rule group name', + name: 'rsa.misc.rule_group', + type: 'keyword', + }, + 'rsa.misc.risk_num': { + category: 'rsa', + description: 'This key captures a Numeric Risk value', + name: 'rsa.misc.risk_num', + type: 'double', + }, + 'rsa.misc.trigger_val': { + category: 'rsa', + description: 'This key captures the Value of the trigger or threshold condition.', + name: 'rsa.misc.trigger_val', + type: 'keyword', + }, + 'rsa.misc.log_session_id1': { + category: 'rsa', + description: + 'This key is used to capture a Linked (Related) Session ID from the session directly', + name: 'rsa.misc.log_session_id1', + type: 'keyword', + }, + 'rsa.misc.comp_version': { + category: 'rsa', + description: 'This key captures the Version level of a sub-component of a product.', + name: 'rsa.misc.comp_version', + type: 'keyword', + }, + 'rsa.misc.content_version': { + category: 'rsa', + description: 'This key captures Version level of a signature or database content.', + name: 'rsa.misc.content_version', + type: 'keyword', + }, + 'rsa.misc.hardware_id': { + category: 'rsa', + description: + 'This key is used to capture unique identifier for a device or system (NOT a Mac address)', + name: 'rsa.misc.hardware_id', + type: 'keyword', + }, + 'rsa.misc.risk': { + category: 'rsa', + description: 'This key captures the non-numeric risk value', + name: 'rsa.misc.risk', + type: 'keyword', + }, + 'rsa.misc.event_id': { + category: 'rsa', + name: 'rsa.misc.event_id', + type: 'keyword', + }, + 'rsa.misc.reason': { + category: 'rsa', + name: 'rsa.misc.reason', + type: 'keyword', + }, + 'rsa.misc.status': { + category: 'rsa', + name: 'rsa.misc.status', + type: 'keyword', + }, + 'rsa.misc.mail_id': { + category: 'rsa', + description: 'This key is used to capture the mailbox id/name', + name: 'rsa.misc.mail_id', + type: 'keyword', + }, + 'rsa.misc.rule_uid': { + category: 'rsa', + description: 'This key is the Unique Identifier for a rule.', + name: 'rsa.misc.rule_uid', + type: 'keyword', + }, + 'rsa.misc.trigger_desc': { + category: 'rsa', + description: 'This key captures the Description of the trigger or threshold condition.', + name: 'rsa.misc.trigger_desc', + type: 'keyword', + }, + 'rsa.misc.inout': { + category: 'rsa', + name: 'rsa.misc.inout', + type: 'keyword', + }, + 'rsa.misc.p_msgid': { + category: 'rsa', + name: 'rsa.misc.p_msgid', + type: 'keyword', + }, + 'rsa.misc.data_type': { + category: 'rsa', + name: 'rsa.misc.data_type', + type: 'keyword', + }, + 'rsa.misc.msgIdPart4': { + category: 'rsa', + name: 'rsa.misc.msgIdPart4', + type: 'keyword', + }, + 'rsa.misc.error': { + category: 'rsa', + description: 'This key captures All non successful Error codes or responses', + name: 'rsa.misc.error', + type: 'keyword', + }, + 'rsa.misc.index': { + category: 'rsa', + name: 'rsa.misc.index', + type: 'keyword', + }, + 'rsa.misc.listnum': { + category: 'rsa', + description: + 'This key is used to capture listname or listnumber, primarily for collecting access-list', + name: 'rsa.misc.listnum', + type: 'keyword', + }, + 'rsa.misc.ntype': { + category: 'rsa', + name: 'rsa.misc.ntype', + type: 'keyword', + }, + 'rsa.misc.observed_val': { + category: 'rsa', + description: + 'This key captures the Value observed (from the perspective of the device generating the log).', + name: 'rsa.misc.observed_val', + type: 'keyword', + }, + 'rsa.misc.policy_value': { + category: 'rsa', + description: + 'This key captures the contents of the policy. This contains details about the policy', + name: 'rsa.misc.policy_value', + type: 'keyword', + }, + 'rsa.misc.pool_name': { + category: 'rsa', + description: 'This key captures the name of a resource pool', + name: 'rsa.misc.pool_name', + type: 'keyword', + }, + 'rsa.misc.rule_template': { + category: 'rsa', + description: + 'A default set of parameters which are overlayed onto a rule (or rulename) which efffectively constitutes a template', + name: 'rsa.misc.rule_template', + type: 'keyword', + }, + 'rsa.misc.count': { + category: 'rsa', + name: 'rsa.misc.count', + type: 'keyword', + }, + 'rsa.misc.number': { + category: 'rsa', + name: 'rsa.misc.number', + type: 'keyword', + }, + 'rsa.misc.sigcat': { + category: 'rsa', + name: 'rsa.misc.sigcat', + type: 'keyword', + }, + 'rsa.misc.type': { + category: 'rsa', + name: 'rsa.misc.type', + type: 'keyword', + }, + 'rsa.misc.comments': { + category: 'rsa', + description: 'Comment information provided in the log message', + name: 'rsa.misc.comments', + type: 'keyword', + }, + 'rsa.misc.doc_number': { + category: 'rsa', + description: 'This key captures File Identification number', + name: 'rsa.misc.doc_number', + type: 'long', + }, + 'rsa.misc.expected_val': { + category: 'rsa', + description: + 'This key captures the Value expected (from the perspective of the device generating the log).', + name: 'rsa.misc.expected_val', + type: 'keyword', + }, + 'rsa.misc.job_num': { + category: 'rsa', + description: 'This key captures the Job Number', + name: 'rsa.misc.job_num', + type: 'keyword', + }, + 'rsa.misc.spi_dst': { + category: 'rsa', + description: 'Destination SPI Index', + name: 'rsa.misc.spi_dst', + type: 'keyword', + }, + 'rsa.misc.spi_src': { + category: 'rsa', + description: 'Source SPI Index', + name: 'rsa.misc.spi_src', + type: 'keyword', + }, + 'rsa.misc.code': { + category: 'rsa', + name: 'rsa.misc.code', + type: 'keyword', + }, + 'rsa.misc.agent_id': { + category: 'rsa', + description: 'This key is used to capture agent id', + name: 'rsa.misc.agent_id', + type: 'keyword', + }, + 'rsa.misc.message_body': { + category: 'rsa', + description: 'This key captures the The contents of the message body.', + name: 'rsa.misc.message_body', + type: 'keyword', + }, + 'rsa.misc.phone': { + category: 'rsa', + name: 'rsa.misc.phone', + type: 'keyword', + }, + 'rsa.misc.sig_id_str': { + category: 'rsa', + description: 'This key captures a string object of the sigid variable.', + name: 'rsa.misc.sig_id_str', + type: 'keyword', + }, + 'rsa.misc.cmd': { + category: 'rsa', + name: 'rsa.misc.cmd', + type: 'keyword', + }, + 'rsa.misc.misc': { + category: 'rsa', + name: 'rsa.misc.misc', + type: 'keyword', + }, + 'rsa.misc.name': { + category: 'rsa', + name: 'rsa.misc.name', + type: 'keyword', + }, + 'rsa.misc.cpu': { + category: 'rsa', + description: 'This key is the CPU time used in the execution of the event being recorded.', + name: 'rsa.misc.cpu', + type: 'long', + }, + 'rsa.misc.event_desc': { + category: 'rsa', + description: + 'This key is used to capture a description of an event available directly or inferred', + name: 'rsa.misc.event_desc', + type: 'keyword', + }, + 'rsa.misc.sig_id1': { + category: 'rsa', + description: 'This key captures IDS/IPS Int Signature ID. This must be linked to the sig.id', + name: 'rsa.misc.sig_id1', + type: 'long', + }, + 'rsa.misc.im_buddyid': { + category: 'rsa', + name: 'rsa.misc.im_buddyid', + type: 'keyword', + }, + 'rsa.misc.im_client': { + category: 'rsa', + name: 'rsa.misc.im_client', + type: 'keyword', + }, + 'rsa.misc.im_userid': { + category: 'rsa', + name: 'rsa.misc.im_userid', + type: 'keyword', + }, + 'rsa.misc.pid': { + category: 'rsa', + name: 'rsa.misc.pid', + type: 'keyword', + }, + 'rsa.misc.priority': { + category: 'rsa', + name: 'rsa.misc.priority', + type: 'keyword', + }, + 'rsa.misc.context_subject': { + category: 'rsa', + description: + 'This key is to be used in an audit context where the subject is the object being identified', + name: 'rsa.misc.context_subject', + type: 'keyword', + }, + 'rsa.misc.context_target': { + category: 'rsa', + name: 'rsa.misc.context_target', + type: 'keyword', + }, + 'rsa.misc.cve': { + category: 'rsa', + description: + 'This key captures CVE (Common Vulnerabilities and Exposures) - an identifier for known information security vulnerabilities.', + name: 'rsa.misc.cve', + type: 'keyword', + }, + 'rsa.misc.fcatnum': { + category: 'rsa', + description: 'This key captures Filter Category Number. Legacy Usage', + name: 'rsa.misc.fcatnum', + type: 'keyword', + }, + 'rsa.misc.library': { + category: 'rsa', + description: 'This key is used to capture library information in mainframe devices', + name: 'rsa.misc.library', + type: 'keyword', + }, + 'rsa.misc.parent_node': { + category: 'rsa', + description: 'This key captures the Parent Node Name. Must be related to node variable.', + name: 'rsa.misc.parent_node', + type: 'keyword', + }, + 'rsa.misc.risk_info': { + category: 'rsa', + description: 'Deprecated, use New Hunting Model (inv.*, ioc, boc, eoc, analysis.*)', + name: 'rsa.misc.risk_info', + type: 'keyword', + }, + 'rsa.misc.tcp_flags': { + category: 'rsa', + description: 'This key is captures the TCP flags set in any packet of session', + name: 'rsa.misc.tcp_flags', + type: 'long', + }, + 'rsa.misc.tos': { + category: 'rsa', + description: 'This key describes the type of service', + name: 'rsa.misc.tos', + type: 'long', + }, + 'rsa.misc.vm_target': { + category: 'rsa', + description: 'VMWare Target **VMWARE** only varaible.', + name: 'rsa.misc.vm_target', + type: 'keyword', + }, + 'rsa.misc.workspace': { + category: 'rsa', + description: 'This key captures Workspace Description', + name: 'rsa.misc.workspace', + type: 'keyword', + }, + 'rsa.misc.command': { + category: 'rsa', + name: 'rsa.misc.command', + type: 'keyword', + }, + 'rsa.misc.event_category': { + category: 'rsa', + name: 'rsa.misc.event_category', + type: 'keyword', + }, + 'rsa.misc.facilityname': { + category: 'rsa', + name: 'rsa.misc.facilityname', + type: 'keyword', + }, + 'rsa.misc.forensic_info': { + category: 'rsa', + name: 'rsa.misc.forensic_info', + type: 'keyword', + }, + 'rsa.misc.jobname': { + category: 'rsa', + name: 'rsa.misc.jobname', + type: 'keyword', + }, + 'rsa.misc.mode': { + category: 'rsa', + name: 'rsa.misc.mode', + type: 'keyword', + }, + 'rsa.misc.policy': { + category: 'rsa', + name: 'rsa.misc.policy', + type: 'keyword', + }, + 'rsa.misc.policy_waiver': { + category: 'rsa', + name: 'rsa.misc.policy_waiver', + type: 'keyword', + }, + 'rsa.misc.second': { + category: 'rsa', + name: 'rsa.misc.second', + type: 'keyword', + }, + 'rsa.misc.space1': { + category: 'rsa', + name: 'rsa.misc.space1', + type: 'keyword', + }, + 'rsa.misc.subcategory': { + category: 'rsa', + name: 'rsa.misc.subcategory', + type: 'keyword', + }, + 'rsa.misc.tbdstr2': { + category: 'rsa', + name: 'rsa.misc.tbdstr2', + type: 'keyword', + }, + 'rsa.misc.alert_id': { + category: 'rsa', + description: 'Deprecated, New Hunting Model (inv.*, ioc, boc, eoc, analysis.*)', + name: 'rsa.misc.alert_id', + type: 'keyword', + }, + 'rsa.misc.checksum_dst': { + category: 'rsa', + description: + 'This key is used to capture the checksum or hash of the the target entity such as a process or file.', + name: 'rsa.misc.checksum_dst', + type: 'keyword', + }, + 'rsa.misc.checksum_src': { + category: 'rsa', + description: + 'This key is used to capture the checksum or hash of the source entity such as a file or process.', + name: 'rsa.misc.checksum_src', + type: 'keyword', + }, + 'rsa.misc.fresult': { + category: 'rsa', + description: 'This key captures the Filter Result', + name: 'rsa.misc.fresult', + type: 'long', + }, + 'rsa.misc.payload_dst': { + category: 'rsa', + description: 'This key is used to capture destination payload', + name: 'rsa.misc.payload_dst', + type: 'keyword', + }, + 'rsa.misc.payload_src': { + category: 'rsa', + description: 'This key is used to capture source payload', + name: 'rsa.misc.payload_src', + type: 'keyword', + }, + 'rsa.misc.pool_id': { + category: 'rsa', + description: 'This key captures the identifier (typically numeric field) of a resource pool', + name: 'rsa.misc.pool_id', + type: 'keyword', + }, + 'rsa.misc.process_id_val': { + category: 'rsa', + description: 'This key is a failure key for Process ID when it is not an integer value', + name: 'rsa.misc.process_id_val', + type: 'keyword', + }, + 'rsa.misc.risk_num_comm': { + category: 'rsa', + description: 'This key captures Risk Number Community', + name: 'rsa.misc.risk_num_comm', + type: 'double', + }, + 'rsa.misc.risk_num_next': { + category: 'rsa', + description: 'This key captures Risk Number NextGen', + name: 'rsa.misc.risk_num_next', + type: 'double', + }, + 'rsa.misc.risk_num_sand': { + category: 'rsa', + description: 'This key captures Risk Number SandBox', + name: 'rsa.misc.risk_num_sand', + type: 'double', + }, + 'rsa.misc.risk_num_static': { + category: 'rsa', + description: 'This key captures Risk Number Static', + name: 'rsa.misc.risk_num_static', + type: 'double', + }, + 'rsa.misc.risk_suspicious': { + category: 'rsa', + description: 'Deprecated, use New Hunting Model (inv.*, ioc, boc, eoc, analysis.*)', + name: 'rsa.misc.risk_suspicious', + type: 'keyword', + }, + 'rsa.misc.risk_warning': { + category: 'rsa', + description: 'Deprecated, use New Hunting Model (inv.*, ioc, boc, eoc, analysis.*)', + name: 'rsa.misc.risk_warning', + type: 'keyword', + }, + 'rsa.misc.snmp_oid': { + category: 'rsa', + description: 'SNMP Object Identifier', + name: 'rsa.misc.snmp_oid', + type: 'keyword', + }, + 'rsa.misc.sql': { + category: 'rsa', + description: 'This key captures the SQL query', + name: 'rsa.misc.sql', + type: 'keyword', + }, + 'rsa.misc.vuln_ref': { + category: 'rsa', + description: 'This key captures the Vulnerability Reference details', + name: 'rsa.misc.vuln_ref', + type: 'keyword', + }, + 'rsa.misc.acl_id': { + category: 'rsa', + name: 'rsa.misc.acl_id', + type: 'keyword', + }, + 'rsa.misc.acl_op': { + category: 'rsa', + name: 'rsa.misc.acl_op', + type: 'keyword', + }, + 'rsa.misc.acl_pos': { + category: 'rsa', + name: 'rsa.misc.acl_pos', + type: 'keyword', + }, + 'rsa.misc.acl_table': { + category: 'rsa', + name: 'rsa.misc.acl_table', + type: 'keyword', + }, + 'rsa.misc.admin': { + category: 'rsa', + name: 'rsa.misc.admin', + type: 'keyword', + }, + 'rsa.misc.alarm_id': { + category: 'rsa', + name: 'rsa.misc.alarm_id', + type: 'keyword', + }, + 'rsa.misc.alarmname': { + category: 'rsa', + name: 'rsa.misc.alarmname', + type: 'keyword', + }, + 'rsa.misc.app_id': { + category: 'rsa', + name: 'rsa.misc.app_id', + type: 'keyword', + }, + 'rsa.misc.audit': { + category: 'rsa', + name: 'rsa.misc.audit', + type: 'keyword', + }, + 'rsa.misc.audit_object': { + category: 'rsa', + name: 'rsa.misc.audit_object', + type: 'keyword', + }, + 'rsa.misc.auditdata': { + category: 'rsa', + name: 'rsa.misc.auditdata', + type: 'keyword', + }, + 'rsa.misc.benchmark': { + category: 'rsa', + name: 'rsa.misc.benchmark', + type: 'keyword', + }, + 'rsa.misc.bypass': { + category: 'rsa', + name: 'rsa.misc.bypass', + type: 'keyword', + }, + 'rsa.misc.cache': { + category: 'rsa', + name: 'rsa.misc.cache', + type: 'keyword', + }, + 'rsa.misc.cache_hit': { + category: 'rsa', + name: 'rsa.misc.cache_hit', + type: 'keyword', + }, + 'rsa.misc.cefversion': { + category: 'rsa', + name: 'rsa.misc.cefversion', + type: 'keyword', + }, + 'rsa.misc.cfg_attr': { + category: 'rsa', + name: 'rsa.misc.cfg_attr', + type: 'keyword', + }, + 'rsa.misc.cfg_obj': { + category: 'rsa', + name: 'rsa.misc.cfg_obj', + type: 'keyword', + }, + 'rsa.misc.cfg_path': { + category: 'rsa', + name: 'rsa.misc.cfg_path', + type: 'keyword', + }, + 'rsa.misc.changes': { + category: 'rsa', + name: 'rsa.misc.changes', + type: 'keyword', + }, + 'rsa.misc.client_ip': { + category: 'rsa', + name: 'rsa.misc.client_ip', + type: 'keyword', + }, + 'rsa.misc.clustermembers': { + category: 'rsa', + name: 'rsa.misc.clustermembers', + type: 'keyword', + }, + 'rsa.misc.cn_acttimeout': { + category: 'rsa', + name: 'rsa.misc.cn_acttimeout', + type: 'keyword', + }, + 'rsa.misc.cn_asn_src': { + category: 'rsa', + name: 'rsa.misc.cn_asn_src', + type: 'keyword', + }, + 'rsa.misc.cn_bgpv4nxthop': { + category: 'rsa', + name: 'rsa.misc.cn_bgpv4nxthop', + type: 'keyword', + }, + 'rsa.misc.cn_ctr_dst_code': { + category: 'rsa', + name: 'rsa.misc.cn_ctr_dst_code', + type: 'keyword', + }, + 'rsa.misc.cn_dst_tos': { + category: 'rsa', + name: 'rsa.misc.cn_dst_tos', + type: 'keyword', + }, + 'rsa.misc.cn_dst_vlan': { + category: 'rsa', + name: 'rsa.misc.cn_dst_vlan', + type: 'keyword', + }, + 'rsa.misc.cn_engine_id': { + category: 'rsa', + name: 'rsa.misc.cn_engine_id', + type: 'keyword', + }, + 'rsa.misc.cn_engine_type': { + category: 'rsa', + name: 'rsa.misc.cn_engine_type', + type: 'keyword', + }, + 'rsa.misc.cn_f_switch': { + category: 'rsa', + name: 'rsa.misc.cn_f_switch', + type: 'keyword', + }, + 'rsa.misc.cn_flowsampid': { + category: 'rsa', + name: 'rsa.misc.cn_flowsampid', + type: 'keyword', + }, + 'rsa.misc.cn_flowsampintv': { + category: 'rsa', + name: 'rsa.misc.cn_flowsampintv', + type: 'keyword', + }, + 'rsa.misc.cn_flowsampmode': { + category: 'rsa', + name: 'rsa.misc.cn_flowsampmode', + type: 'keyword', + }, + 'rsa.misc.cn_inacttimeout': { + category: 'rsa', + name: 'rsa.misc.cn_inacttimeout', + type: 'keyword', + }, + 'rsa.misc.cn_inpermbyts': { + category: 'rsa', + name: 'rsa.misc.cn_inpermbyts', + type: 'keyword', + }, + 'rsa.misc.cn_inpermpckts': { + category: 'rsa', + name: 'rsa.misc.cn_inpermpckts', + type: 'keyword', + }, + 'rsa.misc.cn_invalid': { + category: 'rsa', + name: 'rsa.misc.cn_invalid', + type: 'keyword', + }, + 'rsa.misc.cn_ip_proto_ver': { + category: 'rsa', + name: 'rsa.misc.cn_ip_proto_ver', + type: 'keyword', + }, + 'rsa.misc.cn_ipv4_ident': { + category: 'rsa', + name: 'rsa.misc.cn_ipv4_ident', + type: 'keyword', + }, + 'rsa.misc.cn_l_switch': { + category: 'rsa', + name: 'rsa.misc.cn_l_switch', + type: 'keyword', + }, + 'rsa.misc.cn_log_did': { + category: 'rsa', + name: 'rsa.misc.cn_log_did', + type: 'keyword', + }, + 'rsa.misc.cn_log_rid': { + category: 'rsa', + name: 'rsa.misc.cn_log_rid', + type: 'keyword', + }, + 'rsa.misc.cn_max_ttl': { + category: 'rsa', + name: 'rsa.misc.cn_max_ttl', + type: 'keyword', + }, + 'rsa.misc.cn_maxpcktlen': { + category: 'rsa', + name: 'rsa.misc.cn_maxpcktlen', + type: 'keyword', + }, + 'rsa.misc.cn_min_ttl': { + category: 'rsa', + name: 'rsa.misc.cn_min_ttl', + type: 'keyword', + }, + 'rsa.misc.cn_minpcktlen': { + category: 'rsa', + name: 'rsa.misc.cn_minpcktlen', + type: 'keyword', + }, + 'rsa.misc.cn_mpls_lbl_1': { + category: 'rsa', + name: 'rsa.misc.cn_mpls_lbl_1', + type: 'keyword', + }, + 'rsa.misc.cn_mpls_lbl_10': { + category: 'rsa', + name: 'rsa.misc.cn_mpls_lbl_10', + type: 'keyword', + }, + 'rsa.misc.cn_mpls_lbl_2': { + category: 'rsa', + name: 'rsa.misc.cn_mpls_lbl_2', + type: 'keyword', + }, + 'rsa.misc.cn_mpls_lbl_3': { + category: 'rsa', + name: 'rsa.misc.cn_mpls_lbl_3', + type: 'keyword', + }, + 'rsa.misc.cn_mpls_lbl_4': { + category: 'rsa', + name: 'rsa.misc.cn_mpls_lbl_4', + type: 'keyword', + }, + 'rsa.misc.cn_mpls_lbl_5': { + category: 'rsa', + name: 'rsa.misc.cn_mpls_lbl_5', + type: 'keyword', + }, + 'rsa.misc.cn_mpls_lbl_6': { + category: 'rsa', + name: 'rsa.misc.cn_mpls_lbl_6', + type: 'keyword', + }, + 'rsa.misc.cn_mpls_lbl_7': { + category: 'rsa', + name: 'rsa.misc.cn_mpls_lbl_7', + type: 'keyword', + }, + 'rsa.misc.cn_mpls_lbl_8': { + category: 'rsa', + name: 'rsa.misc.cn_mpls_lbl_8', + type: 'keyword', + }, + 'rsa.misc.cn_mpls_lbl_9': { + category: 'rsa', + name: 'rsa.misc.cn_mpls_lbl_9', + type: 'keyword', + }, + 'rsa.misc.cn_mplstoplabel': { + category: 'rsa', + name: 'rsa.misc.cn_mplstoplabel', + type: 'keyword', + }, + 'rsa.misc.cn_mplstoplabip': { + category: 'rsa', + name: 'rsa.misc.cn_mplstoplabip', + type: 'keyword', + }, + 'rsa.misc.cn_mul_dst_byt': { + category: 'rsa', + name: 'rsa.misc.cn_mul_dst_byt', + type: 'keyword', + }, + 'rsa.misc.cn_mul_dst_pks': { + category: 'rsa', + name: 'rsa.misc.cn_mul_dst_pks', + type: 'keyword', + }, + 'rsa.misc.cn_muligmptype': { + category: 'rsa', + name: 'rsa.misc.cn_muligmptype', + type: 'keyword', + }, + 'rsa.misc.cn_sampalgo': { + category: 'rsa', + name: 'rsa.misc.cn_sampalgo', + type: 'keyword', + }, + 'rsa.misc.cn_sampint': { + category: 'rsa', + name: 'rsa.misc.cn_sampint', + type: 'keyword', + }, + 'rsa.misc.cn_seqctr': { + category: 'rsa', + name: 'rsa.misc.cn_seqctr', + type: 'keyword', + }, + 'rsa.misc.cn_spackets': { + category: 'rsa', + name: 'rsa.misc.cn_spackets', + type: 'keyword', + }, + 'rsa.misc.cn_src_tos': { + category: 'rsa', + name: 'rsa.misc.cn_src_tos', + type: 'keyword', + }, + 'rsa.misc.cn_src_vlan': { + category: 'rsa', + name: 'rsa.misc.cn_src_vlan', + type: 'keyword', + }, + 'rsa.misc.cn_sysuptime': { + category: 'rsa', + name: 'rsa.misc.cn_sysuptime', + type: 'keyword', + }, + 'rsa.misc.cn_template_id': { + category: 'rsa', + name: 'rsa.misc.cn_template_id', + type: 'keyword', + }, + 'rsa.misc.cn_totbytsexp': { + category: 'rsa', + name: 'rsa.misc.cn_totbytsexp', + type: 'keyword', + }, + 'rsa.misc.cn_totflowexp': { + category: 'rsa', + name: 'rsa.misc.cn_totflowexp', + type: 'keyword', + }, + 'rsa.misc.cn_totpcktsexp': { + category: 'rsa', + name: 'rsa.misc.cn_totpcktsexp', + type: 'keyword', + }, + 'rsa.misc.cn_unixnanosecs': { + category: 'rsa', + name: 'rsa.misc.cn_unixnanosecs', + type: 'keyword', + }, + 'rsa.misc.cn_v6flowlabel': { + category: 'rsa', + name: 'rsa.misc.cn_v6flowlabel', + type: 'keyword', + }, + 'rsa.misc.cn_v6optheaders': { + category: 'rsa', + name: 'rsa.misc.cn_v6optheaders', + type: 'keyword', + }, + 'rsa.misc.comp_class': { + category: 'rsa', + name: 'rsa.misc.comp_class', + type: 'keyword', + }, + 'rsa.misc.comp_name': { + category: 'rsa', + name: 'rsa.misc.comp_name', + type: 'keyword', + }, + 'rsa.misc.comp_rbytes': { + category: 'rsa', + name: 'rsa.misc.comp_rbytes', + type: 'keyword', + }, + 'rsa.misc.comp_sbytes': { + category: 'rsa', + name: 'rsa.misc.comp_sbytes', + type: 'keyword', + }, + 'rsa.misc.cpu_data': { + category: 'rsa', + name: 'rsa.misc.cpu_data', + type: 'keyword', + }, + 'rsa.misc.criticality': { + category: 'rsa', + name: 'rsa.misc.criticality', + type: 'keyword', + }, + 'rsa.misc.cs_agency_dst': { + category: 'rsa', + name: 'rsa.misc.cs_agency_dst', + type: 'keyword', + }, + 'rsa.misc.cs_analyzedby': { + category: 'rsa', + name: 'rsa.misc.cs_analyzedby', + type: 'keyword', + }, + 'rsa.misc.cs_av_other': { + category: 'rsa', + name: 'rsa.misc.cs_av_other', + type: 'keyword', + }, + 'rsa.misc.cs_av_primary': { + category: 'rsa', + name: 'rsa.misc.cs_av_primary', + type: 'keyword', + }, + 'rsa.misc.cs_av_secondary': { + category: 'rsa', + name: 'rsa.misc.cs_av_secondary', + type: 'keyword', + }, + 'rsa.misc.cs_bgpv6nxthop': { + category: 'rsa', + name: 'rsa.misc.cs_bgpv6nxthop', + type: 'keyword', + }, + 'rsa.misc.cs_bit9status': { + category: 'rsa', + name: 'rsa.misc.cs_bit9status', + type: 'keyword', + }, + 'rsa.misc.cs_context': { + category: 'rsa', + name: 'rsa.misc.cs_context', + type: 'keyword', + }, + 'rsa.misc.cs_control': { + category: 'rsa', + name: 'rsa.misc.cs_control', + type: 'keyword', + }, + 'rsa.misc.cs_data': { + category: 'rsa', + name: 'rsa.misc.cs_data', + type: 'keyword', + }, + 'rsa.misc.cs_datecret': { + category: 'rsa', + name: 'rsa.misc.cs_datecret', + type: 'keyword', + }, + 'rsa.misc.cs_dst_tld': { + category: 'rsa', + name: 'rsa.misc.cs_dst_tld', + type: 'keyword', + }, + 'rsa.misc.cs_eth_dst_ven': { + category: 'rsa', + name: 'rsa.misc.cs_eth_dst_ven', + type: 'keyword', + }, + 'rsa.misc.cs_eth_src_ven': { + category: 'rsa', + name: 'rsa.misc.cs_eth_src_ven', + type: 'keyword', + }, + 'rsa.misc.cs_event_uuid': { + category: 'rsa', + name: 'rsa.misc.cs_event_uuid', + type: 'keyword', + }, + 'rsa.misc.cs_filetype': { + category: 'rsa', + name: 'rsa.misc.cs_filetype', + type: 'keyword', + }, + 'rsa.misc.cs_fld': { + category: 'rsa', + name: 'rsa.misc.cs_fld', + type: 'keyword', + }, + 'rsa.misc.cs_if_desc': { + category: 'rsa', + name: 'rsa.misc.cs_if_desc', + type: 'keyword', + }, + 'rsa.misc.cs_if_name': { + category: 'rsa', + name: 'rsa.misc.cs_if_name', + type: 'keyword', + }, + 'rsa.misc.cs_ip_next_hop': { + category: 'rsa', + name: 'rsa.misc.cs_ip_next_hop', + type: 'keyword', + }, + 'rsa.misc.cs_ipv4dstpre': { + category: 'rsa', + name: 'rsa.misc.cs_ipv4dstpre', + type: 'keyword', + }, + 'rsa.misc.cs_ipv4srcpre': { + category: 'rsa', + name: 'rsa.misc.cs_ipv4srcpre', + type: 'keyword', + }, + 'rsa.misc.cs_lifetime': { + category: 'rsa', + name: 'rsa.misc.cs_lifetime', + type: 'keyword', + }, + 'rsa.misc.cs_log_medium': { + category: 'rsa', + name: 'rsa.misc.cs_log_medium', + type: 'keyword', + }, + 'rsa.misc.cs_loginname': { + category: 'rsa', + name: 'rsa.misc.cs_loginname', + type: 'keyword', + }, + 'rsa.misc.cs_modulescore': { + category: 'rsa', + name: 'rsa.misc.cs_modulescore', + type: 'keyword', + }, + 'rsa.misc.cs_modulesign': { + category: 'rsa', + name: 'rsa.misc.cs_modulesign', + type: 'keyword', + }, + 'rsa.misc.cs_opswatresult': { + category: 'rsa', + name: 'rsa.misc.cs_opswatresult', + type: 'keyword', + }, + 'rsa.misc.cs_payload': { + category: 'rsa', + name: 'rsa.misc.cs_payload', + type: 'keyword', + }, + 'rsa.misc.cs_registrant': { + category: 'rsa', + name: 'rsa.misc.cs_registrant', + type: 'keyword', + }, + 'rsa.misc.cs_registrar': { + category: 'rsa', + name: 'rsa.misc.cs_registrar', + type: 'keyword', + }, + 'rsa.misc.cs_represult': { + category: 'rsa', + name: 'rsa.misc.cs_represult', + type: 'keyword', + }, + 'rsa.misc.cs_rpayload': { + category: 'rsa', + name: 'rsa.misc.cs_rpayload', + type: 'keyword', + }, + 'rsa.misc.cs_sampler_name': { + category: 'rsa', + name: 'rsa.misc.cs_sampler_name', + type: 'keyword', + }, + 'rsa.misc.cs_sourcemodule': { + category: 'rsa', + name: 'rsa.misc.cs_sourcemodule', + type: 'keyword', + }, + 'rsa.misc.cs_streams': { + category: 'rsa', + name: 'rsa.misc.cs_streams', + type: 'keyword', + }, + 'rsa.misc.cs_targetmodule': { + category: 'rsa', + name: 'rsa.misc.cs_targetmodule', + type: 'keyword', + }, + 'rsa.misc.cs_v6nxthop': { + category: 'rsa', + name: 'rsa.misc.cs_v6nxthop', + type: 'keyword', + }, + 'rsa.misc.cs_whois_server': { + category: 'rsa', + name: 'rsa.misc.cs_whois_server', + type: 'keyword', + }, + 'rsa.misc.cs_yararesult': { + category: 'rsa', + name: 'rsa.misc.cs_yararesult', + type: 'keyword', + }, + 'rsa.misc.description': { + category: 'rsa', + name: 'rsa.misc.description', + type: 'keyword', + }, + 'rsa.misc.devvendor': { + category: 'rsa', + name: 'rsa.misc.devvendor', + type: 'keyword', + }, + 'rsa.misc.distance': { + category: 'rsa', + name: 'rsa.misc.distance', + type: 'keyword', + }, + 'rsa.misc.dstburb': { + category: 'rsa', + name: 'rsa.misc.dstburb', + type: 'keyword', + }, + 'rsa.misc.edomain': { + category: 'rsa', + name: 'rsa.misc.edomain', + type: 'keyword', + }, + 'rsa.misc.edomaub': { + category: 'rsa', + name: 'rsa.misc.edomaub', + type: 'keyword', + }, + 'rsa.misc.euid': { + category: 'rsa', + name: 'rsa.misc.euid', + type: 'keyword', + }, + 'rsa.misc.facility': { + category: 'rsa', + name: 'rsa.misc.facility', + type: 'keyword', + }, + 'rsa.misc.finterface': { + category: 'rsa', + name: 'rsa.misc.finterface', + type: 'keyword', + }, + 'rsa.misc.flags': { + category: 'rsa', + name: 'rsa.misc.flags', + type: 'keyword', + }, + 'rsa.misc.gaddr': { + category: 'rsa', + name: 'rsa.misc.gaddr', + type: 'keyword', + }, + 'rsa.misc.id3': { + category: 'rsa', + name: 'rsa.misc.id3', + type: 'keyword', + }, + 'rsa.misc.im_buddyname': { + category: 'rsa', + name: 'rsa.misc.im_buddyname', + type: 'keyword', + }, + 'rsa.misc.im_croomid': { + category: 'rsa', + name: 'rsa.misc.im_croomid', + type: 'keyword', + }, + 'rsa.misc.im_croomtype': { + category: 'rsa', + name: 'rsa.misc.im_croomtype', + type: 'keyword', + }, + 'rsa.misc.im_members': { + category: 'rsa', + name: 'rsa.misc.im_members', + type: 'keyword', + }, + 'rsa.misc.im_username': { + category: 'rsa', + name: 'rsa.misc.im_username', + type: 'keyword', + }, + 'rsa.misc.ipkt': { + category: 'rsa', + name: 'rsa.misc.ipkt', + type: 'keyword', + }, + 'rsa.misc.ipscat': { + category: 'rsa', + name: 'rsa.misc.ipscat', + type: 'keyword', + }, + 'rsa.misc.ipspri': { + category: 'rsa', + name: 'rsa.misc.ipspri', + type: 'keyword', + }, + 'rsa.misc.latitude': { + category: 'rsa', + name: 'rsa.misc.latitude', + type: 'keyword', + }, + 'rsa.misc.linenum': { + category: 'rsa', + name: 'rsa.misc.linenum', + type: 'keyword', + }, + 'rsa.misc.list_name': { + category: 'rsa', + name: 'rsa.misc.list_name', + type: 'keyword', + }, + 'rsa.misc.load_data': { + category: 'rsa', + name: 'rsa.misc.load_data', + type: 'keyword', + }, + 'rsa.misc.location_floor': { + category: 'rsa', + name: 'rsa.misc.location_floor', + type: 'keyword', + }, + 'rsa.misc.location_mark': { + category: 'rsa', + name: 'rsa.misc.location_mark', + type: 'keyword', + }, + 'rsa.misc.log_id': { + category: 'rsa', + name: 'rsa.misc.log_id', + type: 'keyword', + }, + 'rsa.misc.log_type': { + category: 'rsa', + name: 'rsa.misc.log_type', + type: 'keyword', + }, + 'rsa.misc.logid': { + category: 'rsa', + name: 'rsa.misc.logid', + type: 'keyword', + }, + 'rsa.misc.logip': { + category: 'rsa', + name: 'rsa.misc.logip', + type: 'keyword', + }, + 'rsa.misc.logname': { + category: 'rsa', + name: 'rsa.misc.logname', + type: 'keyword', + }, + 'rsa.misc.longitude': { + category: 'rsa', + name: 'rsa.misc.longitude', + type: 'keyword', + }, + 'rsa.misc.lport': { + category: 'rsa', + name: 'rsa.misc.lport', + type: 'keyword', + }, + 'rsa.misc.mbug_data': { + category: 'rsa', + name: 'rsa.misc.mbug_data', + type: 'keyword', + }, + 'rsa.misc.misc_name': { + category: 'rsa', + name: 'rsa.misc.misc_name', + type: 'keyword', + }, + 'rsa.misc.msg_type': { + category: 'rsa', + name: 'rsa.misc.msg_type', + type: 'keyword', + }, + 'rsa.misc.msgid': { + category: 'rsa', + name: 'rsa.misc.msgid', + type: 'keyword', + }, + 'rsa.misc.netsessid': { + category: 'rsa', + name: 'rsa.misc.netsessid', + type: 'keyword', + }, + 'rsa.misc.num': { + category: 'rsa', + name: 'rsa.misc.num', + type: 'keyword', + }, + 'rsa.misc.number1': { + category: 'rsa', + name: 'rsa.misc.number1', + type: 'keyword', + }, + 'rsa.misc.number2': { + category: 'rsa', + name: 'rsa.misc.number2', + type: 'keyword', + }, + 'rsa.misc.nwwn': { + category: 'rsa', + name: 'rsa.misc.nwwn', + type: 'keyword', + }, + 'rsa.misc.object': { + category: 'rsa', + name: 'rsa.misc.object', + type: 'keyword', + }, + 'rsa.misc.operation': { + category: 'rsa', + name: 'rsa.misc.operation', + type: 'keyword', + }, + 'rsa.misc.opkt': { + category: 'rsa', + name: 'rsa.misc.opkt', + type: 'keyword', + }, + 'rsa.misc.orig_from': { + category: 'rsa', + name: 'rsa.misc.orig_from', + type: 'keyword', + }, + 'rsa.misc.owner_id': { + category: 'rsa', + name: 'rsa.misc.owner_id', + type: 'keyword', + }, + 'rsa.misc.p_action': { + category: 'rsa', + name: 'rsa.misc.p_action', + type: 'keyword', + }, + 'rsa.misc.p_filter': { + category: 'rsa', + name: 'rsa.misc.p_filter', + type: 'keyword', + }, + 'rsa.misc.p_group_object': { + category: 'rsa', + name: 'rsa.misc.p_group_object', + type: 'keyword', + }, + 'rsa.misc.p_id': { + category: 'rsa', + name: 'rsa.misc.p_id', + type: 'keyword', + }, + 'rsa.misc.p_msgid1': { + category: 'rsa', + name: 'rsa.misc.p_msgid1', + type: 'keyword', + }, + 'rsa.misc.p_msgid2': { + category: 'rsa', + name: 'rsa.misc.p_msgid2', + type: 'keyword', + }, + 'rsa.misc.p_result1': { + category: 'rsa', + name: 'rsa.misc.p_result1', + type: 'keyword', + }, + 'rsa.misc.password_chg': { + category: 'rsa', + name: 'rsa.misc.password_chg', + type: 'keyword', + }, + 'rsa.misc.password_expire': { + category: 'rsa', + name: 'rsa.misc.password_expire', + type: 'keyword', + }, + 'rsa.misc.permgranted': { + category: 'rsa', + name: 'rsa.misc.permgranted', + type: 'keyword', + }, + 'rsa.misc.permwanted': { + category: 'rsa', + name: 'rsa.misc.permwanted', + type: 'keyword', + }, + 'rsa.misc.pgid': { + category: 'rsa', + name: 'rsa.misc.pgid', + type: 'keyword', + }, + 'rsa.misc.policyUUID': { + category: 'rsa', + name: 'rsa.misc.policyUUID', + type: 'keyword', + }, + 'rsa.misc.prog_asp_num': { + category: 'rsa', + name: 'rsa.misc.prog_asp_num', + type: 'keyword', + }, + 'rsa.misc.program': { + category: 'rsa', + name: 'rsa.misc.program', + type: 'keyword', + }, + 'rsa.misc.real_data': { + category: 'rsa', + name: 'rsa.misc.real_data', + type: 'keyword', + }, + 'rsa.misc.rec_asp_device': { + category: 'rsa', + name: 'rsa.misc.rec_asp_device', + type: 'keyword', + }, + 'rsa.misc.rec_asp_num': { + category: 'rsa', + name: 'rsa.misc.rec_asp_num', + type: 'keyword', + }, + 'rsa.misc.rec_library': { + category: 'rsa', + name: 'rsa.misc.rec_library', + type: 'keyword', + }, + 'rsa.misc.recordnum': { + category: 'rsa', + name: 'rsa.misc.recordnum', + type: 'keyword', + }, + 'rsa.misc.ruid': { + category: 'rsa', + name: 'rsa.misc.ruid', + type: 'keyword', + }, + 'rsa.misc.sburb': { + category: 'rsa', + name: 'rsa.misc.sburb', + type: 'keyword', + }, + 'rsa.misc.sdomain_fld': { + category: 'rsa', + name: 'rsa.misc.sdomain_fld', + type: 'keyword', + }, + 'rsa.misc.sec': { + category: 'rsa', + name: 'rsa.misc.sec', + type: 'keyword', + }, + 'rsa.misc.sensorname': { + category: 'rsa', + name: 'rsa.misc.sensorname', + type: 'keyword', + }, + 'rsa.misc.seqnum': { + category: 'rsa', + name: 'rsa.misc.seqnum', + type: 'keyword', + }, + 'rsa.misc.session': { + category: 'rsa', + name: 'rsa.misc.session', + type: 'keyword', + }, + 'rsa.misc.sessiontype': { + category: 'rsa', + name: 'rsa.misc.sessiontype', + type: 'keyword', + }, + 'rsa.misc.sigUUID': { + category: 'rsa', + name: 'rsa.misc.sigUUID', + type: 'keyword', + }, + 'rsa.misc.spi': { + category: 'rsa', + name: 'rsa.misc.spi', + type: 'keyword', + }, + 'rsa.misc.srcburb': { + category: 'rsa', + name: 'rsa.misc.srcburb', + type: 'keyword', + }, + 'rsa.misc.srcdom': { + category: 'rsa', + name: 'rsa.misc.srcdom', + type: 'keyword', + }, + 'rsa.misc.srcservice': { + category: 'rsa', + name: 'rsa.misc.srcservice', + type: 'keyword', + }, + 'rsa.misc.state': { + category: 'rsa', + name: 'rsa.misc.state', + type: 'keyword', + }, + 'rsa.misc.status1': { + category: 'rsa', + name: 'rsa.misc.status1', + type: 'keyword', + }, + 'rsa.misc.svcno': { + category: 'rsa', + name: 'rsa.misc.svcno', + type: 'keyword', + }, + 'rsa.misc.system': { + category: 'rsa', + name: 'rsa.misc.system', + type: 'keyword', + }, + 'rsa.misc.tbdstr1': { + category: 'rsa', + name: 'rsa.misc.tbdstr1', + type: 'keyword', + }, + 'rsa.misc.tgtdom': { + category: 'rsa', + name: 'rsa.misc.tgtdom', + type: 'keyword', + }, + 'rsa.misc.tgtdomain': { + category: 'rsa', + name: 'rsa.misc.tgtdomain', + type: 'keyword', + }, + 'rsa.misc.threshold': { + category: 'rsa', + name: 'rsa.misc.threshold', + type: 'keyword', + }, + 'rsa.misc.type1': { + category: 'rsa', + name: 'rsa.misc.type1', + type: 'keyword', + }, + 'rsa.misc.udb_class': { + category: 'rsa', + name: 'rsa.misc.udb_class', + type: 'keyword', + }, + 'rsa.misc.url_fld': { + category: 'rsa', + name: 'rsa.misc.url_fld', + type: 'keyword', + }, + 'rsa.misc.user_div': { + category: 'rsa', + name: 'rsa.misc.user_div', + type: 'keyword', + }, + 'rsa.misc.userid': { + category: 'rsa', + name: 'rsa.misc.userid', + type: 'keyword', + }, + 'rsa.misc.username_fld': { + category: 'rsa', + name: 'rsa.misc.username_fld', + type: 'keyword', + }, + 'rsa.misc.utcstamp': { + category: 'rsa', + name: 'rsa.misc.utcstamp', + type: 'keyword', + }, + 'rsa.misc.v_instafname': { + category: 'rsa', + name: 'rsa.misc.v_instafname', + type: 'keyword', + }, + 'rsa.misc.virt_data': { + category: 'rsa', + name: 'rsa.misc.virt_data', + type: 'keyword', + }, + 'rsa.misc.vpnid': { + category: 'rsa', + name: 'rsa.misc.vpnid', + type: 'keyword', + }, + 'rsa.misc.autorun_type': { + category: 'rsa', + description: 'This is used to capture Auto Run type', + name: 'rsa.misc.autorun_type', + type: 'keyword', + }, + 'rsa.misc.cc_number': { + category: 'rsa', + description: 'Valid Credit Card Numbers only', + name: 'rsa.misc.cc_number', + type: 'long', + }, + 'rsa.misc.content': { + category: 'rsa', + description: 'This key captures the content type from protocol headers', + name: 'rsa.misc.content', + type: 'keyword', + }, + 'rsa.misc.ein_number': { + category: 'rsa', + description: 'Employee Identification Numbers only', + name: 'rsa.misc.ein_number', + type: 'long', + }, + 'rsa.misc.found': { + category: 'rsa', + description: 'This is used to capture the results of regex match', + name: 'rsa.misc.found', + type: 'keyword', + }, + 'rsa.misc.language': { + category: 'rsa', + description: 'This is used to capture list of languages the client support and what it prefers', + name: 'rsa.misc.language', + type: 'keyword', + }, + 'rsa.misc.lifetime': { + category: 'rsa', + description: 'This key is used to capture the session lifetime in seconds.', + name: 'rsa.misc.lifetime', + type: 'long', + }, + 'rsa.misc.link': { + category: 'rsa', + description: + 'This key is used to link the sessions together. This key should never be used to parse Meta data from a session (Logs/Packets) Directly, this is a Reserved key in NetWitness', + name: 'rsa.misc.link', + type: 'keyword', + }, + 'rsa.misc.match': { + category: 'rsa', + description: 'This key is for regex match name from search.ini', + name: 'rsa.misc.match', + type: 'keyword', + }, + 'rsa.misc.param_dst': { + category: 'rsa', + description: 'This key captures the command line/launch argument of the target process or file', + name: 'rsa.misc.param_dst', + type: 'keyword', + }, + 'rsa.misc.param_src': { + category: 'rsa', + description: 'This key captures source parameter', + name: 'rsa.misc.param_src', + type: 'keyword', + }, + 'rsa.misc.search_text': { + category: 'rsa', + description: 'This key captures the Search Text used', + name: 'rsa.misc.search_text', + type: 'keyword', + }, + 'rsa.misc.sig_name': { + category: 'rsa', + description: 'This key is used to capture the Signature Name only.', + name: 'rsa.misc.sig_name', + type: 'keyword', + }, + 'rsa.misc.snmp_value': { + category: 'rsa', + description: 'SNMP set request value', + name: 'rsa.misc.snmp_value', + type: 'keyword', + }, + 'rsa.misc.streams': { + category: 'rsa', + description: 'This key captures number of streams in session', + name: 'rsa.misc.streams', + type: 'long', + }, + 'rsa.db.index': { + category: 'rsa', + description: 'This key captures IndexID of the index.', + name: 'rsa.db.index', + type: 'keyword', + }, + 'rsa.db.instance': { + category: 'rsa', + description: 'This key is used to capture the database server instance name', + name: 'rsa.db.instance', + type: 'keyword', + }, + 'rsa.db.database': { + category: 'rsa', + description: + 'This key is used to capture the name of a database or an instance as seen in a session', + name: 'rsa.db.database', + type: 'keyword', + }, + 'rsa.db.transact_id': { + category: 'rsa', + description: 'This key captures the SQL transantion ID of the current session', + name: 'rsa.db.transact_id', + type: 'keyword', + }, + 'rsa.db.permissions': { + category: 'rsa', + description: 'This key captures permission or privilege level assigned to a resource.', + name: 'rsa.db.permissions', + type: 'keyword', + }, + 'rsa.db.table_name': { + category: 'rsa', + description: 'This key is used to capture the table name', + name: 'rsa.db.table_name', + type: 'keyword', + }, + 'rsa.db.db_id': { + category: 'rsa', + description: 'This key is used to capture the unique identifier for a database', + name: 'rsa.db.db_id', + type: 'keyword', + }, + 'rsa.db.db_pid': { + category: 'rsa', + description: 'This key captures the process id of a connection with database server', + name: 'rsa.db.db_pid', + type: 'long', + }, + 'rsa.db.lread': { + category: 'rsa', + description: 'This key is used for the number of logical reads', + name: 'rsa.db.lread', + type: 'long', + }, + 'rsa.db.lwrite': { + category: 'rsa', + description: 'This key is used for the number of logical writes', + name: 'rsa.db.lwrite', + type: 'long', + }, + 'rsa.db.pread': { + category: 'rsa', + description: 'This key is used for the number of physical writes', + name: 'rsa.db.pread', + type: 'long', + }, + 'rsa.network.alias_host': { + category: 'rsa', + description: + 'This key should be used when the source or destination context of a hostname is not clear.Also it captures the Device Hostname. Any Hostname that isnt ad.computer.', + name: 'rsa.network.alias_host', + type: 'keyword', + }, + 'rsa.network.domain': { + category: 'rsa', + name: 'rsa.network.domain', + type: 'keyword', + }, + 'rsa.network.host_dst': { + category: 'rsa', + description: 'This key should only be used when it’s a Destination Hostname', + name: 'rsa.network.host_dst', + type: 'keyword', + }, + 'rsa.network.network_service': { + category: 'rsa', + description: 'This is used to capture layer 7 protocols/service names', + name: 'rsa.network.network_service', + type: 'keyword', + }, + 'rsa.network.interface': { + category: 'rsa', + description: + 'This key should be used when the source or destination context of an interface is not clear', + name: 'rsa.network.interface', + type: 'keyword', + }, + 'rsa.network.network_port': { + category: 'rsa', + description: + 'Deprecated, use port. NOTE: There is a type discrepancy as currently used, TM: Int32, INDEX: UInt64 (why neither chose the correct UInt16?!)', + name: 'rsa.network.network_port', + type: 'long', + }, + 'rsa.network.eth_host': { + category: 'rsa', + description: 'Deprecated, use alias.mac', + name: 'rsa.network.eth_host', + type: 'keyword', + }, + 'rsa.network.sinterface': { + category: 'rsa', + description: 'This key should only be used when it’s a Source Interface', + name: 'rsa.network.sinterface', + type: 'keyword', + }, + 'rsa.network.dinterface': { + category: 'rsa', + description: 'This key should only be used when it’s a Destination Interface', + name: 'rsa.network.dinterface', + type: 'keyword', + }, + 'rsa.network.vlan': { + category: 'rsa', + description: 'This key should only be used to capture the ID of the Virtual LAN', + name: 'rsa.network.vlan', + type: 'long', + }, + 'rsa.network.zone_src': { + category: 'rsa', + description: 'This key should only be used when it’s a Source Zone.', + name: 'rsa.network.zone_src', + type: 'keyword', + }, + 'rsa.network.zone': { + category: 'rsa', + description: + 'This key should be used when the source or destination context of a Zone is not clear', + name: 'rsa.network.zone', + type: 'keyword', + }, + 'rsa.network.zone_dst': { + category: 'rsa', + description: 'This key should only be used when it’s a Destination Zone.', + name: 'rsa.network.zone_dst', + type: 'keyword', + }, + 'rsa.network.gateway': { + category: 'rsa', + description: 'This key is used to capture the IP Address of the gateway', + name: 'rsa.network.gateway', + type: 'keyword', + }, + 'rsa.network.icmp_type': { + category: 'rsa', + description: 'This key is used to capture the ICMP type only', + name: 'rsa.network.icmp_type', + type: 'long', + }, + 'rsa.network.mask': { + category: 'rsa', + description: 'This key is used to capture the device network IPmask.', + name: 'rsa.network.mask', + type: 'keyword', + }, + 'rsa.network.icmp_code': { + category: 'rsa', + description: 'This key is used to capture the ICMP code only', + name: 'rsa.network.icmp_code', + type: 'long', + }, + 'rsa.network.protocol_detail': { + category: 'rsa', + description: 'This key should be used to capture additional protocol information', + name: 'rsa.network.protocol_detail', + type: 'keyword', + }, + 'rsa.network.dmask': { + category: 'rsa', + description: 'This key is used for Destionation Device network mask', + name: 'rsa.network.dmask', + type: 'keyword', + }, + 'rsa.network.port': { + category: 'rsa', + description: + 'This key should only be used to capture a Network Port when the directionality is not clear', + name: 'rsa.network.port', + type: 'long', + }, + 'rsa.network.smask': { + category: 'rsa', + description: 'This key is used for capturing source Network Mask', + name: 'rsa.network.smask', + type: 'keyword', + }, + 'rsa.network.netname': { + category: 'rsa', + description: + 'This key is used to capture the network name associated with an IP range. This is configured by the end user.', + name: 'rsa.network.netname', + type: 'keyword', + }, + 'rsa.network.paddr': { + category: 'rsa', + description: 'Deprecated', + name: 'rsa.network.paddr', + type: 'ip', + }, + 'rsa.network.faddr': { + category: 'rsa', + name: 'rsa.network.faddr', + type: 'keyword', + }, + 'rsa.network.lhost': { + category: 'rsa', + name: 'rsa.network.lhost', + type: 'keyword', + }, + 'rsa.network.origin': { + category: 'rsa', + name: 'rsa.network.origin', + type: 'keyword', + }, + 'rsa.network.remote_domain_id': { + category: 'rsa', + name: 'rsa.network.remote_domain_id', + type: 'keyword', + }, + 'rsa.network.addr': { + category: 'rsa', + name: 'rsa.network.addr', + type: 'keyword', + }, + 'rsa.network.dns_a_record': { + category: 'rsa', + name: 'rsa.network.dns_a_record', + type: 'keyword', + }, + 'rsa.network.dns_ptr_record': { + category: 'rsa', + name: 'rsa.network.dns_ptr_record', + type: 'keyword', + }, + 'rsa.network.fhost': { + category: 'rsa', + name: 'rsa.network.fhost', + type: 'keyword', + }, + 'rsa.network.fport': { + category: 'rsa', + name: 'rsa.network.fport', + type: 'keyword', + }, + 'rsa.network.laddr': { + category: 'rsa', + name: 'rsa.network.laddr', + type: 'keyword', + }, + 'rsa.network.linterface': { + category: 'rsa', + name: 'rsa.network.linterface', + type: 'keyword', + }, + 'rsa.network.phost': { + category: 'rsa', + name: 'rsa.network.phost', + type: 'keyword', + }, + 'rsa.network.ad_computer_dst': { + category: 'rsa', + description: 'Deprecated, use host.dst', + name: 'rsa.network.ad_computer_dst', + type: 'keyword', + }, + 'rsa.network.eth_type': { + category: 'rsa', + description: 'This key is used to capture Ethernet Type, Used for Layer 3 Protocols Only', + name: 'rsa.network.eth_type', + type: 'long', + }, + 'rsa.network.ip_proto': { + category: 'rsa', + description: + 'This key should be used to capture the Protocol number, all the protocol nubers are converted into string in UI', + name: 'rsa.network.ip_proto', + type: 'long', + }, + 'rsa.network.dns_cname_record': { + category: 'rsa', + name: 'rsa.network.dns_cname_record', + type: 'keyword', + }, + 'rsa.network.dns_id': { + category: 'rsa', + name: 'rsa.network.dns_id', + type: 'keyword', + }, + 'rsa.network.dns_opcode': { + category: 'rsa', + name: 'rsa.network.dns_opcode', + type: 'keyword', + }, + 'rsa.network.dns_resp': { + category: 'rsa', + name: 'rsa.network.dns_resp', + type: 'keyword', + }, + 'rsa.network.dns_type': { + category: 'rsa', + name: 'rsa.network.dns_type', + type: 'keyword', + }, + 'rsa.network.domain1': { + category: 'rsa', + name: 'rsa.network.domain1', + type: 'keyword', + }, + 'rsa.network.host_type': { + category: 'rsa', + name: 'rsa.network.host_type', + type: 'keyword', + }, + 'rsa.network.packet_length': { + category: 'rsa', + name: 'rsa.network.packet_length', + type: 'keyword', + }, + 'rsa.network.host_orig': { + category: 'rsa', + description: + 'This is used to capture the original hostname in case of a Forwarding Agent or a Proxy in between.', + name: 'rsa.network.host_orig', + type: 'keyword', + }, + 'rsa.network.rpayload': { + category: 'rsa', + description: + 'This key is used to capture the total number of payload bytes seen in the retransmitted packets.', + name: 'rsa.network.rpayload', + type: 'keyword', + }, + 'rsa.network.vlan_name': { + category: 'rsa', + description: 'This key should only be used to capture the name of the Virtual LAN', + name: 'rsa.network.vlan_name', + type: 'keyword', + }, + 'rsa.investigations.ec_activity': { + category: 'rsa', + description: 'This key captures the particular event activity(Ex:Logoff)', + name: 'rsa.investigations.ec_activity', + type: 'keyword', + }, + 'rsa.investigations.ec_theme': { + category: 'rsa', + description: 'This key captures the Theme of a particular Event(Ex:Authentication)', + name: 'rsa.investigations.ec_theme', + type: 'keyword', + }, + 'rsa.investigations.ec_subject': { + category: 'rsa', + description: 'This key captures the Subject of a particular Event(Ex:User)', + name: 'rsa.investigations.ec_subject', + type: 'keyword', + }, + 'rsa.investigations.ec_outcome': { + category: 'rsa', + description: 'This key captures the outcome of a particular Event(Ex:Success)', + name: 'rsa.investigations.ec_outcome', + type: 'keyword', + }, + 'rsa.investigations.event_cat': { + category: 'rsa', + description: 'This key captures the Event category number', + name: 'rsa.investigations.event_cat', + type: 'long', + }, + 'rsa.investigations.event_cat_name': { + category: 'rsa', + description: 'This key captures the event category name corresponding to the event cat code', + name: 'rsa.investigations.event_cat_name', + type: 'keyword', + }, + 'rsa.investigations.event_vcat': { + category: 'rsa', + description: + 'This is a vendor supplied category. This should be used in situations where the vendor has adopted their own event_category taxonomy.', + name: 'rsa.investigations.event_vcat', + type: 'keyword', + }, + 'rsa.investigations.analysis_file': { + category: 'rsa', + description: + 'This is used to capture all indicators used in a File Analysis. This key should be used to capture an analysis of a file', + name: 'rsa.investigations.analysis_file', + type: 'keyword', + }, + 'rsa.investigations.analysis_service': { + category: 'rsa', + description: + 'This is used to capture all indicators used in a Service Analysis. This key should be used to capture an analysis of a service', + name: 'rsa.investigations.analysis_service', + type: 'keyword', + }, + 'rsa.investigations.analysis_session': { + category: 'rsa', + description: + 'This is used to capture all indicators used for a Session Analysis. This key should be used to capture an analysis of a session', + name: 'rsa.investigations.analysis_session', + type: 'keyword', + }, + 'rsa.investigations.boc': { + category: 'rsa', + description: 'This is used to capture behaviour of compromise', + name: 'rsa.investigations.boc', + type: 'keyword', + }, + 'rsa.investigations.eoc': { + category: 'rsa', + description: 'This is used to capture Enablers of Compromise', + name: 'rsa.investigations.eoc', + type: 'keyword', + }, + 'rsa.investigations.inv_category': { + category: 'rsa', + description: 'This used to capture investigation category', + name: 'rsa.investigations.inv_category', + type: 'keyword', + }, + 'rsa.investigations.inv_context': { + category: 'rsa', + description: 'This used to capture investigation context', + name: 'rsa.investigations.inv_context', + type: 'keyword', + }, + 'rsa.investigations.ioc': { + category: 'rsa', + description: 'This is key capture indicator of compromise', + name: 'rsa.investigations.ioc', + type: 'keyword', + }, + 'rsa.counters.dclass_c1': { + category: 'rsa', + description: + 'This is a generic counter key that should be used with the label dclass.c1.str only', + name: 'rsa.counters.dclass_c1', + type: 'long', + }, + 'rsa.counters.dclass_c2': { + category: 'rsa', + description: + 'This is a generic counter key that should be used with the label dclass.c2.str only', + name: 'rsa.counters.dclass_c2', + type: 'long', + }, + 'rsa.counters.event_counter': { + category: 'rsa', + description: 'This is used to capture the number of times an event repeated', + name: 'rsa.counters.event_counter', + type: 'long', + }, + 'rsa.counters.dclass_r1': { + category: 'rsa', + description: + 'This is a generic ratio key that should be used with the label dclass.r1.str only', + name: 'rsa.counters.dclass_r1', + type: 'keyword', + }, + 'rsa.counters.dclass_c3': { + category: 'rsa', + description: + 'This is a generic counter key that should be used with the label dclass.c3.str only', + name: 'rsa.counters.dclass_c3', + type: 'long', + }, + 'rsa.counters.dclass_c1_str': { + category: 'rsa', + description: + 'This is a generic counter string key that should be used with the label dclass.c1 only', + name: 'rsa.counters.dclass_c1_str', + type: 'keyword', + }, + 'rsa.counters.dclass_c2_str': { + category: 'rsa', + description: + 'This is a generic counter string key that should be used with the label dclass.c2 only', + name: 'rsa.counters.dclass_c2_str', + type: 'keyword', + }, + 'rsa.counters.dclass_r1_str': { + category: 'rsa', + description: + 'This is a generic ratio string key that should be used with the label dclass.r1 only', + name: 'rsa.counters.dclass_r1_str', + type: 'keyword', + }, + 'rsa.counters.dclass_r2': { + category: 'rsa', + description: + 'This is a generic ratio key that should be used with the label dclass.r2.str only', + name: 'rsa.counters.dclass_r2', + type: 'keyword', + }, + 'rsa.counters.dclass_c3_str': { + category: 'rsa', + description: + 'This is a generic counter string key that should be used with the label dclass.c3 only', + name: 'rsa.counters.dclass_c3_str', + type: 'keyword', + }, + 'rsa.counters.dclass_r3': { + category: 'rsa', + description: + 'This is a generic ratio key that should be used with the label dclass.r3.str only', + name: 'rsa.counters.dclass_r3', + type: 'keyword', + }, + 'rsa.counters.dclass_r2_str': { + category: 'rsa', + description: + 'This is a generic ratio string key that should be used with the label dclass.r2 only', + name: 'rsa.counters.dclass_r2_str', + type: 'keyword', + }, + 'rsa.counters.dclass_r3_str': { + category: 'rsa', + description: + 'This is a generic ratio string key that should be used with the label dclass.r3 only', + name: 'rsa.counters.dclass_r3_str', + type: 'keyword', + }, + 'rsa.identity.auth_method': { + category: 'rsa', + description: 'This key is used to capture authentication methods used only', + name: 'rsa.identity.auth_method', + type: 'keyword', + }, + 'rsa.identity.user_role': { + category: 'rsa', + description: 'This key is used to capture the Role of a user only', + name: 'rsa.identity.user_role', + type: 'keyword', + }, + 'rsa.identity.dn': { + category: 'rsa', + description: 'X.500 (LDAP) Distinguished Name', + name: 'rsa.identity.dn', + type: 'keyword', + }, + 'rsa.identity.logon_type': { + category: 'rsa', + description: 'This key is used to capture the type of logon method used.', + name: 'rsa.identity.logon_type', + type: 'keyword', + }, + 'rsa.identity.profile': { + category: 'rsa', + description: 'This key is used to capture the user profile', + name: 'rsa.identity.profile', + type: 'keyword', + }, + 'rsa.identity.accesses': { + category: 'rsa', + description: 'This key is used to capture actual privileges used in accessing an object', + name: 'rsa.identity.accesses', + type: 'keyword', + }, + 'rsa.identity.realm': { + category: 'rsa', + description: 'Radius realm or similar grouping of accounts', + name: 'rsa.identity.realm', + type: 'keyword', + }, + 'rsa.identity.user_sid_dst': { + category: 'rsa', + description: 'This key captures Destination User Session ID', + name: 'rsa.identity.user_sid_dst', + type: 'keyword', + }, + 'rsa.identity.dn_src': { + category: 'rsa', + description: + 'An X.500 (LDAP) Distinguished name that is used in a context that indicates a Source dn', + name: 'rsa.identity.dn_src', + type: 'keyword', + }, + 'rsa.identity.org': { + category: 'rsa', + description: 'This key captures the User organization', + name: 'rsa.identity.org', + type: 'keyword', + }, + 'rsa.identity.dn_dst': { + category: 'rsa', + description: + 'An X.500 (LDAP) Distinguished name that used in a context that indicates a Destination dn', + name: 'rsa.identity.dn_dst', + type: 'keyword', + }, + 'rsa.identity.firstname': { + category: 'rsa', + description: + 'This key is for First Names only, this is used for Healthcare predominantly to capture Patients information', + name: 'rsa.identity.firstname', + type: 'keyword', + }, + 'rsa.identity.lastname': { + category: 'rsa', + description: + 'This key is for Last Names only, this is used for Healthcare predominantly to capture Patients information', + name: 'rsa.identity.lastname', + type: 'keyword', + }, + 'rsa.identity.user_dept': { + category: 'rsa', + description: "User's Department Names only", + name: 'rsa.identity.user_dept', + type: 'keyword', + }, + 'rsa.identity.user_sid_src': { + category: 'rsa', + description: 'This key captures Source User Session ID', + name: 'rsa.identity.user_sid_src', + type: 'keyword', + }, + 'rsa.identity.federated_sp': { + category: 'rsa', + description: + 'This key is the Federated Service Provider. This is the application requesting authentication.', + name: 'rsa.identity.federated_sp', + type: 'keyword', + }, + 'rsa.identity.federated_idp': { + category: 'rsa', + description: + 'This key is the federated Identity Provider. This is the server providing the authentication.', + name: 'rsa.identity.federated_idp', + type: 'keyword', + }, + 'rsa.identity.logon_type_desc': { + category: 'rsa', + description: + "This key is used to capture the textual description of an integer logon type as stored in the meta key 'logon.type'.", + name: 'rsa.identity.logon_type_desc', + type: 'keyword', + }, + 'rsa.identity.middlename': { + category: 'rsa', + description: + 'This key is for Middle Names only, this is used for Healthcare predominantly to capture Patients information', + name: 'rsa.identity.middlename', + type: 'keyword', + }, + 'rsa.identity.password': { + category: 'rsa', + description: 'This key is for Passwords seen in any session, plain text or encrypted', + name: 'rsa.identity.password', + type: 'keyword', + }, + 'rsa.identity.host_role': { + category: 'rsa', + description: 'This key should only be used to capture the role of a Host Machine', + name: 'rsa.identity.host_role', + type: 'keyword', + }, + 'rsa.identity.ldap': { + category: 'rsa', + description: + 'This key is for Uninterpreted LDAP values. Ldap Values that don’t have a clear query or response context', + name: 'rsa.identity.ldap', + type: 'keyword', + }, + 'rsa.identity.ldap_query': { + category: 'rsa', + description: 'This key is the Search criteria from an LDAP search', + name: 'rsa.identity.ldap_query', + type: 'keyword', + }, + 'rsa.identity.ldap_response': { + category: 'rsa', + description: 'This key is to capture Results from an LDAP search', + name: 'rsa.identity.ldap_response', + type: 'keyword', + }, + 'rsa.identity.owner': { + category: 'rsa', + description: + 'This is used to capture username the process or service is running as, the author of the task', + name: 'rsa.identity.owner', + type: 'keyword', + }, + 'rsa.identity.service_account': { + category: 'rsa', + description: + 'This key is a windows specific key, used for capturing name of the account a service (referenced in the event) is running under. Legacy Usage', + name: 'rsa.identity.service_account', + type: 'keyword', + }, + 'rsa.email.email_dst': { + category: 'rsa', + description: + 'This key is used to capture the Destination email address only, when the destination context is not clear use email', + name: 'rsa.email.email_dst', + type: 'keyword', + }, + 'rsa.email.email_src': { + category: 'rsa', + description: + 'This key is used to capture the source email address only, when the source context is not clear use email', + name: 'rsa.email.email_src', + type: 'keyword', + }, + 'rsa.email.subject': { + category: 'rsa', + description: 'This key is used to capture the subject string from an Email only.', + name: 'rsa.email.subject', + type: 'keyword', + }, + 'rsa.email.email': { + category: 'rsa', + description: + 'This key is used to capture a generic email address where the source or destination context is not clear', + name: 'rsa.email.email', + type: 'keyword', + }, + 'rsa.email.trans_from': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.email.trans_from', + type: 'keyword', + }, + 'rsa.email.trans_to': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.email.trans_to', + type: 'keyword', + }, + 'rsa.file.privilege': { + category: 'rsa', + description: 'Deprecated, use permissions', + name: 'rsa.file.privilege', + type: 'keyword', + }, + 'rsa.file.attachment': { + category: 'rsa', + description: 'This key captures the attachment file name', + name: 'rsa.file.attachment', + type: 'keyword', + }, + 'rsa.file.filesystem': { + category: 'rsa', + name: 'rsa.file.filesystem', + type: 'keyword', + }, + 'rsa.file.binary': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.file.binary', + type: 'keyword', + }, + 'rsa.file.filename_dst': { + category: 'rsa', + description: 'This is used to capture name of the file targeted by the action', + name: 'rsa.file.filename_dst', + type: 'keyword', + }, + 'rsa.file.filename_src': { + category: 'rsa', + description: + 'This is used to capture name of the parent filename, the file which performed the action', + name: 'rsa.file.filename_src', + type: 'keyword', + }, + 'rsa.file.filename_tmp': { + category: 'rsa', + name: 'rsa.file.filename_tmp', + type: 'keyword', + }, + 'rsa.file.directory_dst': { + category: 'rsa', + description: + 'This key is used to capture the directory of the target process or file', + name: 'rsa.file.directory_dst', + type: 'keyword', + }, + 'rsa.file.directory_src': { + category: 'rsa', + description: 'This key is used to capture the directory of the source process or file', + name: 'rsa.file.directory_src', + type: 'keyword', + }, + 'rsa.file.file_entropy': { + category: 'rsa', + description: 'This is used to capture entropy vale of a file', + name: 'rsa.file.file_entropy', + type: 'double', + }, + 'rsa.file.file_vendor': { + category: 'rsa', + description: 'This is used to capture Company name of file located in version_info', + name: 'rsa.file.file_vendor', + type: 'keyword', + }, + 'rsa.file.task_name': { + category: 'rsa', + description: 'This is used to capture name of the task', + name: 'rsa.file.task_name', + type: 'keyword', + }, + 'rsa.web.fqdn': { + category: 'rsa', + description: 'Fully Qualified Domain Names', + name: 'rsa.web.fqdn', + type: 'keyword', + }, + 'rsa.web.web_cookie': { + category: 'rsa', + description: 'This key is used to capture the Web cookies specifically.', + name: 'rsa.web.web_cookie', + type: 'keyword', + }, + 'rsa.web.alias_host': { + category: 'rsa', + name: 'rsa.web.alias_host', + type: 'keyword', + }, + 'rsa.web.reputation_num': { + category: 'rsa', + description: 'Reputation Number of an entity. Typically used for Web Domains', + name: 'rsa.web.reputation_num', + type: 'double', + }, + 'rsa.web.web_ref_domain': { + category: 'rsa', + description: "Web referer's domain", + name: 'rsa.web.web_ref_domain', + type: 'keyword', + }, + 'rsa.web.web_ref_query': { + category: 'rsa', + description: "This key captures Web referer's query portion of the URL", + name: 'rsa.web.web_ref_query', + type: 'keyword', + }, + 'rsa.web.remote_domain': { + category: 'rsa', + name: 'rsa.web.remote_domain', + type: 'keyword', + }, + 'rsa.web.web_ref_page': { + category: 'rsa', + description: "This key captures Web referer's page information", + name: 'rsa.web.web_ref_page', + type: 'keyword', + }, + 'rsa.web.web_ref_root': { + category: 'rsa', + description: "Web referer's root URL path", + name: 'rsa.web.web_ref_root', + type: 'keyword', + }, + 'rsa.web.cn_asn_dst': { + category: 'rsa', + name: 'rsa.web.cn_asn_dst', + type: 'keyword', + }, + 'rsa.web.cn_rpackets': { + category: 'rsa', + name: 'rsa.web.cn_rpackets', + type: 'keyword', + }, + 'rsa.web.urlpage': { + category: 'rsa', + name: 'rsa.web.urlpage', + type: 'keyword', + }, + 'rsa.web.urlroot': { + category: 'rsa', + name: 'rsa.web.urlroot', + type: 'keyword', + }, + 'rsa.web.p_url': { + category: 'rsa', + name: 'rsa.web.p_url', + type: 'keyword', + }, + 'rsa.web.p_user_agent': { + category: 'rsa', + name: 'rsa.web.p_user_agent', + type: 'keyword', + }, + 'rsa.web.p_web_cookie': { + category: 'rsa', + name: 'rsa.web.p_web_cookie', + type: 'keyword', + }, + 'rsa.web.p_web_method': { + category: 'rsa', + name: 'rsa.web.p_web_method', + type: 'keyword', + }, + 'rsa.web.p_web_referer': { + category: 'rsa', + name: 'rsa.web.p_web_referer', + type: 'keyword', + }, + 'rsa.web.web_extension_tmp': { + category: 'rsa', + name: 'rsa.web.web_extension_tmp', + type: 'keyword', + }, + 'rsa.web.web_page': { + category: 'rsa', + name: 'rsa.web.web_page', + type: 'keyword', + }, + 'rsa.threat.threat_category': { + category: 'rsa', + description: 'This key captures Threat Name/Threat Category/Categorization of alert', + name: 'rsa.threat.threat_category', + type: 'keyword', + }, + 'rsa.threat.threat_desc': { + category: 'rsa', + description: + 'This key is used to capture the threat description from the session directly or inferred', + name: 'rsa.threat.threat_desc', + type: 'keyword', + }, + 'rsa.threat.alert': { + category: 'rsa', + description: 'This key is used to capture name of the alert', + name: 'rsa.threat.alert', + type: 'keyword', + }, + 'rsa.threat.threat_source': { + category: 'rsa', + description: 'This key is used to capture source of the threat', + name: 'rsa.threat.threat_source', + type: 'keyword', + }, + 'rsa.crypto.crypto': { + category: 'rsa', + description: 'This key is used to capture the Encryption Type or Encryption Key only', + name: 'rsa.crypto.crypto', + type: 'keyword', + }, + 'rsa.crypto.cipher_src': { + category: 'rsa', + description: 'This key is for Source (Client) Cipher', + name: 'rsa.crypto.cipher_src', + type: 'keyword', + }, + 'rsa.crypto.cert_subject': { + category: 'rsa', + description: 'This key is used to capture the Certificate organization only', + name: 'rsa.crypto.cert_subject', + type: 'keyword', + }, + 'rsa.crypto.peer': { + category: 'rsa', + description: "This key is for Encryption peer's IP Address", + name: 'rsa.crypto.peer', + type: 'keyword', + }, + 'rsa.crypto.cipher_size_src': { + category: 'rsa', + description: 'This key captures Source (Client) Cipher Size', + name: 'rsa.crypto.cipher_size_src', + type: 'long', + }, + 'rsa.crypto.ike': { + category: 'rsa', + description: 'IKE negotiation phase.', + name: 'rsa.crypto.ike', + type: 'keyword', + }, + 'rsa.crypto.scheme': { + category: 'rsa', + description: 'This key captures the Encryption scheme used', + name: 'rsa.crypto.scheme', + type: 'keyword', + }, + 'rsa.crypto.peer_id': { + category: 'rsa', + description: 'This key is for Encryption peer’s identity', + name: 'rsa.crypto.peer_id', + type: 'keyword', + }, + 'rsa.crypto.sig_type': { + category: 'rsa', + description: 'This key captures the Signature Type', + name: 'rsa.crypto.sig_type', + type: 'keyword', + }, + 'rsa.crypto.cert_issuer': { + category: 'rsa', + name: 'rsa.crypto.cert_issuer', + type: 'keyword', + }, + 'rsa.crypto.cert_host_name': { + category: 'rsa', + description: 'Deprecated key defined only in table map.', + name: 'rsa.crypto.cert_host_name', + type: 'keyword', + }, + 'rsa.crypto.cert_error': { + category: 'rsa', + description: 'This key captures the Certificate Error String', + name: 'rsa.crypto.cert_error', + type: 'keyword', + }, + 'rsa.crypto.cipher_dst': { + category: 'rsa', + description: 'This key is for Destination (Server) Cipher', + name: 'rsa.crypto.cipher_dst', + type: 'keyword', + }, + 'rsa.crypto.cipher_size_dst': { + category: 'rsa', + description: 'This key captures Destination (Server) Cipher Size', + name: 'rsa.crypto.cipher_size_dst', + type: 'long', + }, + 'rsa.crypto.ssl_ver_src': { + category: 'rsa', + description: 'Deprecated, use version', + name: 'rsa.crypto.ssl_ver_src', + type: 'keyword', + }, + 'rsa.crypto.d_certauth': { + category: 'rsa', + name: 'rsa.crypto.d_certauth', + type: 'keyword', + }, + 'rsa.crypto.s_certauth': { + category: 'rsa', + name: 'rsa.crypto.s_certauth', + type: 'keyword', + }, + 'rsa.crypto.ike_cookie1': { + category: 'rsa', + description: 'ID of the negotiation — sent for ISAKMP Phase One', + name: 'rsa.crypto.ike_cookie1', + type: 'keyword', + }, + 'rsa.crypto.ike_cookie2': { + category: 'rsa', + description: 'ID of the negotiation — sent for ISAKMP Phase Two', + name: 'rsa.crypto.ike_cookie2', + type: 'keyword', + }, + 'rsa.crypto.cert_checksum': { + category: 'rsa', + name: 'rsa.crypto.cert_checksum', + type: 'keyword', + }, + 'rsa.crypto.cert_host_cat': { + category: 'rsa', + description: 'This key is used for the hostname category value of a certificate', + name: 'rsa.crypto.cert_host_cat', + type: 'keyword', + }, + 'rsa.crypto.cert_serial': { + category: 'rsa', + description: 'This key is used to capture the Certificate serial number only', + name: 'rsa.crypto.cert_serial', + type: 'keyword', + }, + 'rsa.crypto.cert_status': { + category: 'rsa', + description: 'This key captures Certificate validation status', + name: 'rsa.crypto.cert_status', + type: 'keyword', + }, + 'rsa.crypto.ssl_ver_dst': { + category: 'rsa', + description: 'Deprecated, use version', + name: 'rsa.crypto.ssl_ver_dst', + type: 'keyword', + }, + 'rsa.crypto.cert_keysize': { + category: 'rsa', + name: 'rsa.crypto.cert_keysize', + type: 'keyword', + }, + 'rsa.crypto.cert_username': { + category: 'rsa', + name: 'rsa.crypto.cert_username', + type: 'keyword', + }, + 'rsa.crypto.https_insact': { + category: 'rsa', + name: 'rsa.crypto.https_insact', + type: 'keyword', + }, + 'rsa.crypto.https_valid': { + category: 'rsa', + name: 'rsa.crypto.https_valid', + type: 'keyword', + }, + 'rsa.crypto.cert_ca': { + category: 'rsa', + description: 'This key is used to capture the Certificate signing authority only', + name: 'rsa.crypto.cert_ca', + type: 'keyword', + }, + 'rsa.crypto.cert_common': { + category: 'rsa', + description: 'This key is used to capture the Certificate common name only', + name: 'rsa.crypto.cert_common', + type: 'keyword', + }, + 'rsa.wireless.wlan_ssid': { + category: 'rsa', + description: 'This key is used to capture the ssid of a Wireless Session', + name: 'rsa.wireless.wlan_ssid', + type: 'keyword', + }, + 'rsa.wireless.access_point': { + category: 'rsa', + description: 'This key is used to capture the access point name.', + name: 'rsa.wireless.access_point', + type: 'keyword', + }, + 'rsa.wireless.wlan_channel': { + category: 'rsa', + description: 'This is used to capture the channel names', + name: 'rsa.wireless.wlan_channel', + type: 'long', + }, + 'rsa.wireless.wlan_name': { + category: 'rsa', + description: 'This key captures either WLAN number/name', + name: 'rsa.wireless.wlan_name', + type: 'keyword', + }, + 'rsa.storage.disk_volume': { + category: 'rsa', + description: 'A unique name assigned to logical units (volumes) within a physical disk', + name: 'rsa.storage.disk_volume', + type: 'keyword', + }, + 'rsa.storage.lun': { + category: 'rsa', + description: 'Logical Unit Number.This key is a very useful concept in Storage.', + name: 'rsa.storage.lun', + type: 'keyword', + }, + 'rsa.storage.pwwn': { + category: 'rsa', + description: 'This uniquely identifies a port on a HBA.', + name: 'rsa.storage.pwwn', + type: 'keyword', + }, + 'rsa.physical.org_dst': { + category: 'rsa', + description: + 'This is used to capture the destination organization based on the GEOPIP Maxmind database.', + name: 'rsa.physical.org_dst', + type: 'keyword', + }, + 'rsa.physical.org_src': { + category: 'rsa', + description: + 'This is used to capture the source organization based on the GEOPIP Maxmind database.', + name: 'rsa.physical.org_src', + type: 'keyword', + }, + 'rsa.healthcare.patient_fname': { + category: 'rsa', + description: + 'This key is for First Names only, this is used for Healthcare predominantly to capture Patients information', + name: 'rsa.healthcare.patient_fname', + type: 'keyword', + }, + 'rsa.healthcare.patient_id': { + category: 'rsa', + description: 'This key captures the unique ID for a patient', + name: 'rsa.healthcare.patient_id', + type: 'keyword', + }, + 'rsa.healthcare.patient_lname': { + category: 'rsa', + description: + 'This key is for Last Names only, this is used for Healthcare predominantly to capture Patients information', + name: 'rsa.healthcare.patient_lname', + type: 'keyword', + }, + 'rsa.healthcare.patient_mname': { + category: 'rsa', + description: + 'This key is for Middle Names only, this is used for Healthcare predominantly to capture Patients information', + name: 'rsa.healthcare.patient_mname', + type: 'keyword', + }, + 'rsa.endpoint.host_state': { + category: 'rsa', + description: + 'This key is used to capture the current state of the machine, such as blacklisted, infected, firewall disabled and so on', + name: 'rsa.endpoint.host_state', + type: 'keyword', + }, + 'rsa.endpoint.registry_key': { + category: 'rsa', + description: 'This key captures the path to the registry key', + name: 'rsa.endpoint.registry_key', + type: 'keyword', + }, + 'rsa.endpoint.registry_value': { + category: 'rsa', + description: 'This key captures values or decorators used within a registry entry', + name: 'rsa.endpoint.registry_value', + type: 'keyword', + }, + 'forcepoint.virus_id': { + category: 'forcepoint', + description: 'Virus ID ', + name: 'forcepoint.virus_id', + type: 'keyword', + }, + 'checkpoint.app_risk': { + category: 'checkpoint', + description: 'Application risk.', + name: 'checkpoint.app_risk', + type: 'keyword', + }, + 'checkpoint.app_severity': { + category: 'checkpoint', + description: 'Application threat severity.', + name: 'checkpoint.app_severity', + type: 'keyword', + }, + 'checkpoint.app_sig_id': { + category: 'checkpoint', + description: 'The signature ID which the application was detected by.', + name: 'checkpoint.app_sig_id', + type: 'keyword', + }, + 'checkpoint.auth_method': { + category: 'checkpoint', + description: 'Password authentication protocol used.', + name: 'checkpoint.auth_method', + type: 'keyword', + }, + 'checkpoint.category': { + category: 'checkpoint', + description: 'Category.', + name: 'checkpoint.category', + type: 'keyword', + }, + 'checkpoint.confidence_level': { + category: 'checkpoint', + description: 'Confidence level determined.', + name: 'checkpoint.confidence_level', + type: 'integer', + }, + 'checkpoint.connectivity_state': { + category: 'checkpoint', + description: 'Connectivity state.', + name: 'checkpoint.connectivity_state', + type: 'keyword', + }, + 'checkpoint.cookie': { + category: 'checkpoint', + description: 'IKE cookie.', + name: 'checkpoint.cookie', + type: 'keyword', + }, + 'checkpoint.dst_phone_number': { + category: 'checkpoint', + description: 'Destination IP-Phone.', + name: 'checkpoint.dst_phone_number', + type: 'keyword', + }, + 'checkpoint.email_control': { + category: 'checkpoint', + description: 'Engine name.', + name: 'checkpoint.email_control', + type: 'keyword', + }, + 'checkpoint.email_id': { + category: 'checkpoint', + description: 'Internal email ID.', + name: 'checkpoint.email_id', + type: 'keyword', + }, + 'checkpoint.email_recipients_num': { + category: 'checkpoint', + description: 'Number of recipients.', + name: 'checkpoint.email_recipients_num', + type: 'long', + }, + 'checkpoint.email_session_id': { + category: 'checkpoint', + description: 'Internal email session ID.', + name: 'checkpoint.email_session_id', + type: 'keyword', + }, + 'checkpoint.email_spool_id': { + category: 'checkpoint', + description: 'Internal email spool ID.', + name: 'checkpoint.email_spool_id', + type: 'keyword', + }, + 'checkpoint.email_subject': { + category: 'checkpoint', + description: 'Email subject.', + name: 'checkpoint.email_subject', + type: 'keyword', + }, + 'checkpoint.event_count': { + category: 'checkpoint', + description: 'Number of events associated with the log.', + name: 'checkpoint.event_count', + type: 'long', + }, + 'checkpoint.frequency': { + category: 'checkpoint', + description: 'Scan frequency.', + name: 'checkpoint.frequency', + type: 'keyword', + }, + 'checkpoint.icmp_type': { + category: 'checkpoint', + description: 'ICMP type.', + name: 'checkpoint.icmp_type', + type: 'long', + }, + 'checkpoint.icmp_code': { + category: 'checkpoint', + description: 'ICMP code.', + name: 'checkpoint.icmp_code', + type: 'long', + }, + 'checkpoint.identity_type': { + category: 'checkpoint', + description: 'Identity type.', + name: 'checkpoint.identity_type', + type: 'keyword', + }, + 'checkpoint.incident_extension': { + category: 'checkpoint', + description: 'Format of original data.', + name: 'checkpoint.incident_extension', + type: 'keyword', + }, + 'checkpoint.integrity_av_invoke_type': { + category: 'checkpoint', + description: 'Scan invoke type.', + name: 'checkpoint.integrity_av_invoke_type', + type: 'keyword', + }, + 'checkpoint.malware_family': { + category: 'checkpoint', + description: 'Malware family.', + name: 'checkpoint.malware_family', + type: 'keyword', + }, + 'checkpoint.peer_gateway': { + category: 'checkpoint', + description: 'Main IP of the peer Security Gateway.', + name: 'checkpoint.peer_gateway', + type: 'ip', + }, + 'checkpoint.performance_impact': { + category: 'checkpoint', + description: 'Protection performance impact.', + name: 'checkpoint.performance_impact', + type: 'integer', + }, + 'checkpoint.protection_id': { + category: 'checkpoint', + description: 'Protection malware ID.', + name: 'checkpoint.protection_id', + type: 'keyword', + }, + 'checkpoint.protection_name': { + category: 'checkpoint', + description: 'Specific signature name of the attack.', + name: 'checkpoint.protection_name', + type: 'keyword', + }, + 'checkpoint.protection_type': { + category: 'checkpoint', + description: 'Type of protection used to detect the attack.', + name: 'checkpoint.protection_type', + type: 'keyword', + }, + 'checkpoint.scan_result': { + category: 'checkpoint', + description: 'Scan result.', + name: 'checkpoint.scan_result', + type: 'keyword', + }, + 'checkpoint.sensor_mode': { + category: 'checkpoint', + description: 'Sensor mode.', + name: 'checkpoint.sensor_mode', + type: 'keyword', + }, + 'checkpoint.severity': { + category: 'checkpoint', + description: 'Threat severity.', + name: 'checkpoint.severity', + type: 'keyword', + }, + 'checkpoint.spyware_name': { + category: 'checkpoint', + description: 'Spyware name.', + name: 'checkpoint.spyware_name', + type: 'keyword', + }, + 'checkpoint.spyware_status': { + category: 'checkpoint', + description: 'Spyware status.', + name: 'checkpoint.spyware_status', + type: 'keyword', + }, + 'checkpoint.subs_exp': { + category: 'checkpoint', + description: 'The expiration date of the subscription.', + name: 'checkpoint.subs_exp', + type: 'date', + }, + 'checkpoint.tcp_flags': { + category: 'checkpoint', + description: 'TCP packet flags.', + name: 'checkpoint.tcp_flags', + type: 'keyword', + }, + 'checkpoint.termination_reason': { + category: 'checkpoint', + description: 'Termination reason.', + name: 'checkpoint.termination_reason', + type: 'keyword', + }, + 'checkpoint.update_status': { + category: 'checkpoint', + description: 'Update status.', + name: 'checkpoint.update_status', + type: 'keyword', + }, + 'checkpoint.user_status': { + category: 'checkpoint', + description: 'User response.', + name: 'checkpoint.user_status', + type: 'keyword', + }, + 'checkpoint.uuid': { + category: 'checkpoint', + description: 'External ID.', + name: 'checkpoint.uuid', + type: 'keyword', + }, + 'checkpoint.virus_name': { + category: 'checkpoint', + description: 'Virus name.', + name: 'checkpoint.virus_name', + type: 'keyword', + }, + 'checkpoint.voip_log_type': { + category: 'checkpoint', + description: 'VoIP log types.', + name: 'checkpoint.voip_log_type', + type: 'keyword', + }, + 'cef.extensions.cp_app_risk': { + category: 'cef', + name: 'cef.extensions.cp_app_risk', + type: 'keyword', + }, + 'cef.extensions.cp_severity': { + category: 'cef', + name: 'cef.extensions.cp_severity', + type: 'keyword', + }, + 'cef.extensions.ifname': { + category: 'cef', + name: 'cef.extensions.ifname', + type: 'keyword', + }, + 'cef.extensions.inzone': { + category: 'cef', + name: 'cef.extensions.inzone', + type: 'keyword', + }, + 'cef.extensions.layer_uuid': { + category: 'cef', + name: 'cef.extensions.layer_uuid', + type: 'keyword', + }, + 'cef.extensions.layer_name': { + category: 'cef', + name: 'cef.extensions.layer_name', + type: 'keyword', + }, + 'cef.extensions.logid': { + category: 'cef', + name: 'cef.extensions.logid', + type: 'keyword', + }, + 'cef.extensions.loguid': { + category: 'cef', + name: 'cef.extensions.loguid', + type: 'keyword', + }, + 'cef.extensions.match_id': { + category: 'cef', + name: 'cef.extensions.match_id', + type: 'keyword', + }, + 'cef.extensions.nat_addtnl_rulenum': { + category: 'cef', + name: 'cef.extensions.nat_addtnl_rulenum', + type: 'keyword', + }, + 'cef.extensions.nat_rulenum': { + category: 'cef', + name: 'cef.extensions.nat_rulenum', + type: 'keyword', + }, + 'cef.extensions.origin': { + category: 'cef', + name: 'cef.extensions.origin', + type: 'keyword', + }, + 'cef.extensions.originsicname': { + category: 'cef', + name: 'cef.extensions.originsicname', + type: 'keyword', + }, + 'cef.extensions.outzone': { + category: 'cef', + name: 'cef.extensions.outzone', + type: 'keyword', + }, + 'cef.extensions.parent_rule': { + category: 'cef', + name: 'cef.extensions.parent_rule', + type: 'keyword', + }, + 'cef.extensions.product': { + category: 'cef', + name: 'cef.extensions.product', + type: 'keyword', + }, + 'cef.extensions.rule_action': { + category: 'cef', + name: 'cef.extensions.rule_action', + type: 'keyword', + }, + 'cef.extensions.rule_uid': { + category: 'cef', + name: 'cef.extensions.rule_uid', + type: 'keyword', + }, + 'cef.extensions.sequencenum': { + category: 'cef', + name: 'cef.extensions.sequencenum', + type: 'keyword', + }, + 'cef.extensions.service_id': { + category: 'cef', + name: 'cef.extensions.service_id', + type: 'keyword', + }, + 'cef.extensions.version': { + category: 'cef', + name: 'cef.extensions.version', + type: 'keyword', + }, + 'checkpoint.calc_desc': { + category: 'checkpoint', + description: 'Log description. ', + name: 'checkpoint.calc_desc', + type: 'keyword', + }, + 'checkpoint.dst_country': { + category: 'checkpoint', + description: 'Destination country. ', + name: 'checkpoint.dst_country', + type: 'keyword', + }, + 'checkpoint.dst_user_name': { + category: 'checkpoint', + description: 'Connected user name on the destination IP. ', + name: 'checkpoint.dst_user_name', + type: 'keyword', + }, + 'checkpoint.sys_message': { + category: 'checkpoint', + description: 'System messages ', + name: 'checkpoint.sys_message', + type: 'keyword', + }, + 'checkpoint.logid': { + category: 'checkpoint', + description: 'System messages ', + name: 'checkpoint.logid', + type: 'keyword', + }, + 'checkpoint.failure_impact': { + category: 'checkpoint', + description: 'The impact of update service failure. ', + name: 'checkpoint.failure_impact', + type: 'keyword', + }, + 'checkpoint.id': { + category: 'checkpoint', + description: 'Override application ID. ', + name: 'checkpoint.id', + type: 'integer', + }, + 'checkpoint.information': { + category: 'checkpoint', + description: 'Policy installation status for a specific blade. ', + name: 'checkpoint.information', + type: 'keyword', + }, + 'checkpoint.layer_name': { + category: 'checkpoint', + description: 'Layer name. ', + name: 'checkpoint.layer_name', + type: 'keyword', + }, + 'checkpoint.layer_uuid': { + category: 'checkpoint', + description: 'Layer UUID. ', + name: 'checkpoint.layer_uuid', + type: 'keyword', + }, + 'checkpoint.log_id': { + category: 'checkpoint', + description: 'Unique identity for logs. ', + name: 'checkpoint.log_id', + type: 'integer', + }, + 'checkpoint.origin_sic_name': { + category: 'checkpoint', + description: 'Machine SIC. ', + name: 'checkpoint.origin_sic_name', + type: 'keyword', + }, + 'checkpoint.policy_mgmt': { + category: 'checkpoint', + description: 'Name of the Management Server that manages this Security Gateway. ', + name: 'checkpoint.policy_mgmt', + type: 'keyword', + }, + 'checkpoint.policy_name': { + category: 'checkpoint', + description: 'Name of the last policy that this Security Gateway fetched. ', + name: 'checkpoint.policy_name', + type: 'keyword', + }, + 'checkpoint.protocol': { + category: 'checkpoint', + description: 'Protocol detected on the connection. ', + name: 'checkpoint.protocol', + type: 'keyword', + }, + 'checkpoint.proxy_src_ip': { + category: 'checkpoint', + description: 'Sender source IP (even when using proxy). ', + name: 'checkpoint.proxy_src_ip', + type: 'ip', + }, + 'checkpoint.rule': { + category: 'checkpoint', + description: 'Matched rule number. ', + name: 'checkpoint.rule', + type: 'integer', + }, + 'checkpoint.rule_action': { + category: 'checkpoint', + description: 'Action of the matched rule in the access policy. ', + name: 'checkpoint.rule_action', + type: 'keyword', + }, + 'checkpoint.scan_direction': { + category: 'checkpoint', + description: 'Scan direction. ', + name: 'checkpoint.scan_direction', + type: 'keyword', + }, + 'checkpoint.session_id': { + category: 'checkpoint', + description: 'Log uuid. ', + name: 'checkpoint.session_id', + type: 'keyword', + }, + 'checkpoint.source_os': { + category: 'checkpoint', + description: 'OS which generated the attack. ', + name: 'checkpoint.source_os', + type: 'keyword', + }, + 'checkpoint.src_country': { + category: 'checkpoint', + description: 'Country name, derived from connection source IP address. ', + name: 'checkpoint.src_country', + type: 'keyword', + }, + 'checkpoint.src_user_name': { + category: 'checkpoint', + description: 'User name connected to source IP ', + name: 'checkpoint.src_user_name', + type: 'keyword', + }, + 'checkpoint.ticket_id': { + category: 'checkpoint', + description: 'Unique ID per file. ', + name: 'checkpoint.ticket_id', + type: 'keyword', + }, + 'checkpoint.tls_server_host_name': { + category: 'checkpoint', + description: 'SNI/CN from encrypted TLS connection used by URLF for categorization. ', + name: 'checkpoint.tls_server_host_name', + type: 'keyword', + }, + 'checkpoint.verdict': { + category: 'checkpoint', + description: 'TE engine verdict Possible values: Malicious/Benign/Error. ', + name: 'checkpoint.verdict', + type: 'keyword', + }, + 'checkpoint.user': { + category: 'checkpoint', + description: 'Source user name. ', + name: 'checkpoint.user', + type: 'keyword', + }, + 'checkpoint.vendor_list': { + category: 'checkpoint', + description: 'The vendor name that provided the verdict for a malicious URL. ', + name: 'checkpoint.vendor_list', + type: 'keyword', + }, + 'checkpoint.web_server_type': { + category: 'checkpoint', + description: 'Web server detected in the HTTP response. ', + name: 'checkpoint.web_server_type', + type: 'keyword', + }, + 'checkpoint.client_name': { + category: 'checkpoint', + description: 'Client Application or Software Blade that detected the event. ', + name: 'checkpoint.client_name', + type: 'keyword', + }, + 'checkpoint.client_version': { + category: 'checkpoint', + description: 'Build version of SandBlast Agent client installed on the computer. ', + name: 'checkpoint.client_version', + type: 'keyword', + }, + 'checkpoint.extension_version': { + category: 'checkpoint', + description: 'Build version of the SandBlast Agent browser extension. ', + name: 'checkpoint.extension_version', + type: 'keyword', + }, + 'checkpoint.host_time': { + category: 'checkpoint', + description: 'Local time on the endpoint computer. ', + name: 'checkpoint.host_time', + type: 'keyword', + }, + 'checkpoint.installed_products': { + category: 'checkpoint', + description: 'List of installed Endpoint Software Blades. ', + name: 'checkpoint.installed_products', + type: 'keyword', + }, + 'checkpoint.cc': { + category: 'checkpoint', + description: 'The Carbon Copy address of the email. ', + name: 'checkpoint.cc', + type: 'keyword', + }, + 'checkpoint.parent_process_username': { + category: 'checkpoint', + description: 'Owner username of the parent process of the process that triggered the attack. ', + name: 'checkpoint.parent_process_username', + type: 'keyword', + }, + 'checkpoint.process_username': { + category: 'checkpoint', + description: 'Owner username of the process that triggered the attack. ', + name: 'checkpoint.process_username', + type: 'keyword', + }, + 'checkpoint.audit_status': { + category: 'checkpoint', + description: 'Audit Status. Can be Success or Failure. ', + name: 'checkpoint.audit_status', + type: 'keyword', + }, + 'checkpoint.objecttable': { + category: 'checkpoint', + description: 'Table of affected objects. ', + name: 'checkpoint.objecttable', + type: 'keyword', + }, + 'checkpoint.objecttype': { + category: 'checkpoint', + description: 'The type of the affected object. ', + name: 'checkpoint.objecttype', + type: 'keyword', + }, + 'checkpoint.operation_number': { + category: 'checkpoint', + description: 'The operation nuber. ', + name: 'checkpoint.operation_number', + type: 'keyword', + }, + 'checkpoint.suppressed_logs': { + category: 'checkpoint', + description: + 'Aggregated connections for five minutes on the same source, destination and port. ', + name: 'checkpoint.suppressed_logs', + type: 'integer', + }, + 'checkpoint.blade_name': { + category: 'checkpoint', + description: 'Blade name. ', + name: 'checkpoint.blade_name', + type: 'keyword', + }, + 'checkpoint.status': { + category: 'checkpoint', + description: 'Ok/Warning/Error. ', + name: 'checkpoint.status', + type: 'keyword', + }, + 'checkpoint.short_desc': { + category: 'checkpoint', + description: 'Short description of the process that was executed. ', + name: 'checkpoint.short_desc', + type: 'keyword', + }, + 'checkpoint.long_desc': { + category: 'checkpoint', + description: 'More information on the process (usually describing error reason in failure). ', + name: 'checkpoint.long_desc', + type: 'keyword', + }, + 'checkpoint.scan_hosts_hour': { + category: 'checkpoint', + description: 'Number of unique hosts during the last hour. ', + name: 'checkpoint.scan_hosts_hour', + type: 'integer', + }, + 'checkpoint.scan_hosts_day': { + category: 'checkpoint', + description: 'Number of unique hosts during the last day. ', + name: 'checkpoint.scan_hosts_day', + type: 'integer', + }, + 'checkpoint.scan_hosts_week': { + category: 'checkpoint', + description: 'Number of unique hosts during the last week. ', + name: 'checkpoint.scan_hosts_week', + type: 'integer', + }, + 'checkpoint.unique_detected_hour': { + category: 'checkpoint', + description: 'Detected virus for a specific host during the last hour. ', + name: 'checkpoint.unique_detected_hour', + type: 'integer', + }, + 'checkpoint.unique_detected_day': { + category: 'checkpoint', + description: 'Detected virus for a specific host during the last day. ', + name: 'checkpoint.unique_detected_day', + type: 'integer', + }, + 'checkpoint.unique_detected_week': { + category: 'checkpoint', + description: 'Detected virus for a specific host during the last week. ', + name: 'checkpoint.unique_detected_week', + type: 'integer', + }, + 'checkpoint.scan_mail': { + category: 'checkpoint', + description: 'Number of emails that were scanned by "AB malicious activity" engine. ', + name: 'checkpoint.scan_mail', + type: 'integer', + }, + 'checkpoint.additional_ip': { + category: 'checkpoint', + description: 'DNS host name. ', + name: 'checkpoint.additional_ip', + type: 'keyword', + }, + 'checkpoint.description': { + category: 'checkpoint', + description: 'Additional explanation how the security gateway enforced the connection. ', + name: 'checkpoint.description', + type: 'keyword', + }, + 'checkpoint.email_spam_category': { + category: 'checkpoint', + description: 'Email categories. Possible values: spam/not spam/phishing. ', + name: 'checkpoint.email_spam_category', + type: 'keyword', + }, + 'checkpoint.email_control_analysis': { + category: 'checkpoint', + description: 'Message classification, received from spam vendor engine. ', + name: 'checkpoint.email_control_analysis', + type: 'keyword', + }, + 'checkpoint.scan_results': { + category: 'checkpoint', + description: '"Infected"/description of a failure. ', + name: 'checkpoint.scan_results', + type: 'keyword', + }, + 'checkpoint.original_queue_id': { + category: 'checkpoint', + description: 'Original postfix email queue id. ', + name: 'checkpoint.original_queue_id', + type: 'keyword', + }, + 'checkpoint.risk': { + category: 'checkpoint', + description: 'Risk level we got from the engine. ', + name: 'checkpoint.risk', + type: 'keyword', + }, + 'checkpoint.observable_name': { + category: 'checkpoint', + description: 'IOC observable signature name. ', + name: 'checkpoint.observable_name', + type: 'keyword', + }, + 'checkpoint.observable_id': { + category: 'checkpoint', + description: 'IOC observable signature id. ', + name: 'checkpoint.observable_id', + type: 'keyword', + }, + 'checkpoint.observable_comment': { + category: 'checkpoint', + description: 'IOC observable signature description. ', + name: 'checkpoint.observable_comment', + type: 'keyword', + }, + 'checkpoint.indicator_name': { + category: 'checkpoint', + description: 'IOC indicator name. ', + name: 'checkpoint.indicator_name', + type: 'keyword', + }, + 'checkpoint.indicator_description': { + category: 'checkpoint', + description: 'IOC indicator description. ', + name: 'checkpoint.indicator_description', + type: 'keyword', + }, + 'checkpoint.indicator_reference': { + category: 'checkpoint', + description: 'IOC indicator reference. ', + name: 'checkpoint.indicator_reference', + type: 'keyword', + }, + 'checkpoint.indicator_uuid': { + category: 'checkpoint', + description: 'IOC indicator uuid. ', + name: 'checkpoint.indicator_uuid', + type: 'keyword', + }, + 'checkpoint.app_desc': { + category: 'checkpoint', + description: 'Application description. ', + name: 'checkpoint.app_desc', + type: 'keyword', + }, + 'checkpoint.app_id': { + category: 'checkpoint', + description: 'Application ID. ', + name: 'checkpoint.app_id', + type: 'integer', + }, + 'checkpoint.certificate_resource': { + category: 'checkpoint', + description: 'HTTPS resource Possible values: SNI or domain name (DN). ', + name: 'checkpoint.certificate_resource', + type: 'keyword', + }, + 'checkpoint.certificate_validation': { + category: 'checkpoint', + description: + 'Precise error, describing HTTPS certificate failure under "HTTPS categorize websites" feature. ', + name: 'checkpoint.certificate_validation', + type: 'keyword', + }, + 'checkpoint.browse_time': { + category: 'checkpoint', + description: 'Application session browse time. ', + name: 'checkpoint.browse_time', + type: 'keyword', + }, + 'checkpoint.limit_requested': { + category: 'checkpoint', + description: 'Indicates whether data limit was requested for the session. ', + name: 'checkpoint.limit_requested', + type: 'integer', + }, + 'checkpoint.limit_applied': { + category: 'checkpoint', + description: 'Indicates whether the session was actually date limited. ', + name: 'checkpoint.limit_applied', + type: 'integer', + }, + 'checkpoint.dropped_total': { + category: 'checkpoint', + description: 'Amount of dropped packets (both incoming and outgoing). ', + name: 'checkpoint.dropped_total', + type: 'integer', + }, + 'checkpoint.client_type_os': { + category: 'checkpoint', + description: 'Client OS detected in the HTTP request. ', + name: 'checkpoint.client_type_os', + type: 'keyword', + }, + 'checkpoint.name': { + category: 'checkpoint', + description: 'Application name. ', + name: 'checkpoint.name', + type: 'keyword', + }, + 'checkpoint.properties': { + category: 'checkpoint', + description: 'Application categories. ', + name: 'checkpoint.properties', + type: 'keyword', + }, + 'checkpoint.sig_id': { + category: 'checkpoint', + description: "Application's signature ID which how it was detected by. ", + name: 'checkpoint.sig_id', + type: 'keyword', + }, + 'checkpoint.desc': { + category: 'checkpoint', + description: 'Override application description. ', + name: 'checkpoint.desc', + type: 'keyword', + }, + 'checkpoint.referrer_self_uid': { + category: 'checkpoint', + description: 'UUID of the current log. ', + name: 'checkpoint.referrer_self_uid', + type: 'keyword', + }, + 'checkpoint.referrer_parent_uid': { + category: 'checkpoint', + description: 'Log UUID of the referring application. ', + name: 'checkpoint.referrer_parent_uid', + type: 'keyword', + }, + 'checkpoint.needs_browse_time': { + category: 'checkpoint', + description: 'Browse time required for the connection. ', + name: 'checkpoint.needs_browse_time', + type: 'integer', + }, + 'checkpoint.cluster_info': { + category: 'checkpoint', + description: + 'Cluster information. Possible options: Failover reason/cluster state changes/CP cluster or 3rd party. ', + name: 'checkpoint.cluster_info', + type: 'keyword', + }, + 'checkpoint.sync': { + category: 'checkpoint', + description: 'Sync status and the reason (stable, at risk). ', + name: 'checkpoint.sync', + type: 'keyword', + }, + 'checkpoint.file_direction': { + category: 'checkpoint', + description: 'File direction. Possible options: upload/download. ', + name: 'checkpoint.file_direction', + type: 'keyword', + }, + 'checkpoint.invalid_file_size': { + category: 'checkpoint', + description: 'File_size field is valid only if this field is set to 0. ', + name: 'checkpoint.invalid_file_size', + type: 'integer', + }, + 'checkpoint.top_archive_file_name': { + category: 'checkpoint', + description: 'In case of archive file: the file that was sent/received. ', + name: 'checkpoint.top_archive_file_name', + type: 'keyword', + }, + 'checkpoint.data_type_name': { + category: 'checkpoint', + description: 'Data type in rulebase that was matched. ', + name: 'checkpoint.data_type_name', + type: 'keyword', + }, + 'checkpoint.specific_data_type_name': { + category: 'checkpoint', + description: 'Compound/Group scenario, data type that was matched. ', + name: 'checkpoint.specific_data_type_name', + type: 'keyword', + }, + 'checkpoint.word_list': { + category: 'checkpoint', + description: 'Words matched by data type. ', + name: 'checkpoint.word_list', + type: 'keyword', + }, + 'checkpoint.info': { + category: 'checkpoint', + description: 'Special log message. ', + name: 'checkpoint.info', + type: 'keyword', + }, + 'checkpoint.outgoing_url': { + category: 'checkpoint', + description: 'URL related to this log (for HTTP). ', + name: 'checkpoint.outgoing_url', + type: 'keyword', + }, + 'checkpoint.dlp_rule_name': { + category: 'checkpoint', + description: 'Matched rule name. ', + name: 'checkpoint.dlp_rule_name', + type: 'keyword', + }, + 'checkpoint.dlp_recipients': { + category: 'checkpoint', + description: 'Mail recipients. ', + name: 'checkpoint.dlp_recipients', + type: 'keyword', + }, + 'checkpoint.dlp_subject': { + category: 'checkpoint', + description: 'Mail subject. ', + name: 'checkpoint.dlp_subject', + type: 'keyword', + }, + 'checkpoint.dlp_word_list': { + category: 'checkpoint', + description: 'Phrases matched by data type. ', + name: 'checkpoint.dlp_word_list', + type: 'keyword', + }, + 'checkpoint.dlp_template_score': { + category: 'checkpoint', + description: 'Template data type match score. ', + name: 'checkpoint.dlp_template_score', + type: 'keyword', + }, + 'checkpoint.message_size': { + category: 'checkpoint', + description: 'Mail/post size. ', + name: 'checkpoint.message_size', + type: 'integer', + }, + 'checkpoint.dlp_incident_uid': { + category: 'checkpoint', + description: 'Unique ID of the matched rule. ', + name: 'checkpoint.dlp_incident_uid', + type: 'keyword', + }, + 'checkpoint.dlp_related_incident_uid': { + category: 'checkpoint', + description: 'Other ID related to this one. ', + name: 'checkpoint.dlp_related_incident_uid', + type: 'keyword', + }, + 'checkpoint.dlp_data_type_name': { + category: 'checkpoint', + description: 'Matched data type. ', + name: 'checkpoint.dlp_data_type_name', + type: 'keyword', + }, + 'checkpoint.dlp_data_type_uid': { + category: 'checkpoint', + description: 'Unique ID of the matched data type. ', + name: 'checkpoint.dlp_data_type_uid', + type: 'keyword', + }, + 'checkpoint.dlp_violation_description': { + category: 'checkpoint', + description: 'Violation descriptions described in the rulebase. ', + name: 'checkpoint.dlp_violation_description', + type: 'keyword', + }, + 'checkpoint.dlp_relevant_data_types': { + category: 'checkpoint', + description: 'In case of Compound/Group: the inner data types that were matched. ', + name: 'checkpoint.dlp_relevant_data_types', + type: 'keyword', + }, + 'checkpoint.dlp_action_reason': { + category: 'checkpoint', + description: 'Action chosen reason. ', + name: 'checkpoint.dlp_action_reason', + type: 'keyword', + }, + 'checkpoint.dlp_categories': { + category: 'checkpoint', + description: 'Data type category. ', + name: 'checkpoint.dlp_categories', + type: 'keyword', + }, + 'checkpoint.dlp_transint': { + category: 'checkpoint', + description: 'HTTP/SMTP/FTP. ', + name: 'checkpoint.dlp_transint', + type: 'keyword', + }, + 'checkpoint.duplicate': { + category: 'checkpoint', + description: + 'Log marked as duplicated, when mail is split and the Security Gateway sees it twice. ', + name: 'checkpoint.duplicate', + type: 'keyword', + }, + 'checkpoint.matched_file': { + category: 'checkpoint', + description: 'Unique ID of the matched data type. ', + name: 'checkpoint.matched_file', + type: 'keyword', + }, + 'checkpoint.matched_file_text_segments': { + category: 'checkpoint', + description: 'Fingerprint: number of text segments matched by this traffic. ', + name: 'checkpoint.matched_file_text_segments', + type: 'integer', + }, + 'checkpoint.matched_file_percentage': { + category: 'checkpoint', + description: 'Fingerprint: match percentage of the traffic. ', + name: 'checkpoint.matched_file_percentage', + type: 'integer', + }, + 'checkpoint.dlp_additional_action': { + category: 'checkpoint', + description: 'Watermark/None. ', + name: 'checkpoint.dlp_additional_action', + type: 'keyword', + }, + 'checkpoint.dlp_watermark_profile': { + category: 'checkpoint', + description: 'Watermark which was applied. ', + name: 'checkpoint.dlp_watermark_profile', + type: 'keyword', + }, + 'checkpoint.dlp_repository_id': { + category: 'checkpoint', + description: 'ID of scanned repository. ', + name: 'checkpoint.dlp_repository_id', + type: 'keyword', + }, + 'checkpoint.dlp_repository_root_path': { + category: 'checkpoint', + description: 'Repository path. ', + name: 'checkpoint.dlp_repository_root_path', + type: 'keyword', + }, + 'checkpoint.scan_id': { + category: 'checkpoint', + description: 'Sequential number of scan. ', + name: 'checkpoint.scan_id', + type: 'keyword', + }, + 'checkpoint.special_properties': { + category: 'checkpoint', + description: + "If this field is set to '1' the log will not be shown (in use for monitoring scan progress). ", + name: 'checkpoint.special_properties', + type: 'integer', + }, + 'checkpoint.dlp_repository_total_size': { + category: 'checkpoint', + description: 'Repository size. ', + name: 'checkpoint.dlp_repository_total_size', + type: 'integer', + }, + 'checkpoint.dlp_repository_files_number': { + category: 'checkpoint', + description: 'Number of files in repository. ', + name: 'checkpoint.dlp_repository_files_number', + type: 'integer', + }, + 'checkpoint.dlp_repository_scanned_files_number': { + category: 'checkpoint', + description: 'Number of scanned files in repository. ', + name: 'checkpoint.dlp_repository_scanned_files_number', + type: 'integer', + }, + 'checkpoint.duration': { + category: 'checkpoint', + description: 'Scan duration. ', + name: 'checkpoint.duration', + type: 'keyword', + }, + 'checkpoint.dlp_fingerprint_long_status': { + category: 'checkpoint', + description: 'Scan status - long format. ', + name: 'checkpoint.dlp_fingerprint_long_status', + type: 'keyword', + }, + 'checkpoint.dlp_fingerprint_short_status': { + category: 'checkpoint', + description: 'Scan status - short format. ', + name: 'checkpoint.dlp_fingerprint_short_status', + type: 'keyword', + }, + 'checkpoint.dlp_repository_directories_number': { + category: 'checkpoint', + description: 'Number of directories in repository. ', + name: 'checkpoint.dlp_repository_directories_number', + type: 'integer', + }, + 'checkpoint.dlp_repository_unreachable_directories_number': { + category: 'checkpoint', + description: 'Number of directories the Security Gateway was unable to read. ', + name: 'checkpoint.dlp_repository_unreachable_directories_number', + type: 'integer', + }, + 'checkpoint.dlp_fingerprint_files_number': { + category: 'checkpoint', + description: 'Number of successfully scanned files in repository. ', + name: 'checkpoint.dlp_fingerprint_files_number', + type: 'integer', + }, + 'checkpoint.dlp_repository_skipped_files_number': { + category: 'checkpoint', + description: 'Skipped number of files because of configuration. ', + name: 'checkpoint.dlp_repository_skipped_files_number', + type: 'integer', + }, + 'checkpoint.dlp_repository_scanned_directories_number': { + category: 'checkpoint', + description: 'Amount of directories scanned. ', + name: 'checkpoint.dlp_repository_scanned_directories_number', + type: 'integer', + }, + 'checkpoint.number_of_errors': { + category: 'checkpoint', + description: 'Number of files that were not scanned due to an error. ', + name: 'checkpoint.number_of_errors', + type: 'integer', + }, + 'checkpoint.next_scheduled_scan_date': { + category: 'checkpoint', + description: 'Next scan scheduled time according to time object. ', + name: 'checkpoint.next_scheduled_scan_date', + type: 'keyword', + }, + 'checkpoint.dlp_repository_scanned_total_size': { + category: 'checkpoint', + description: 'Size scanned. ', + name: 'checkpoint.dlp_repository_scanned_total_size', + type: 'integer', + }, + 'checkpoint.dlp_repository_reached_directories_number': { + category: 'checkpoint', + description: 'Number of scanned directories in repository. ', + name: 'checkpoint.dlp_repository_reached_directories_number', + type: 'integer', + }, + 'checkpoint.dlp_repository_not_scanned_directories_percentage': { + category: 'checkpoint', + description: 'Percentage of directories the Security Gateway was unable to read. ', + name: 'checkpoint.dlp_repository_not_scanned_directories_percentage', + type: 'integer', + }, + 'checkpoint.speed': { + category: 'checkpoint', + description: 'Current scan speed. ', + name: 'checkpoint.speed', + type: 'integer', + }, + 'checkpoint.dlp_repository_scan_progress': { + category: 'checkpoint', + description: 'Scan percentage. ', + name: 'checkpoint.dlp_repository_scan_progress', + type: 'integer', + }, + 'checkpoint.sub_policy_name': { + category: 'checkpoint', + description: 'Layer name. ', + name: 'checkpoint.sub_policy_name', + type: 'keyword', + }, + 'checkpoint.sub_policy_uid': { + category: 'checkpoint', + description: 'Layer uid. ', + name: 'checkpoint.sub_policy_uid', + type: 'keyword', + }, + 'checkpoint.fw_message': { + category: 'checkpoint', + description: 'Used for various firewall errors. ', + name: 'checkpoint.fw_message', + type: 'keyword', + }, + 'checkpoint.message': { + category: 'checkpoint', + description: 'ISP link has failed. ', + name: 'checkpoint.message', + type: 'keyword', + }, + 'checkpoint.isp_link': { + category: 'checkpoint', + description: 'Name of ISP link. ', + name: 'checkpoint.isp_link', + type: 'keyword', + }, + 'checkpoint.fw_subproduct': { + category: 'checkpoint', + description: 'Can be vpn/non vpn. ', + name: 'checkpoint.fw_subproduct', + type: 'keyword', + }, + 'checkpoint.sctp_error': { + category: 'checkpoint', + description: 'Error information, what caused sctp to fail on out_of_state. ', + name: 'checkpoint.sctp_error', + type: 'keyword', + }, + 'checkpoint.chunk_type': { + category: 'checkpoint', + description: 'Chunck of the sctp stream. ', + name: 'checkpoint.chunk_type', + type: 'keyword', + }, + 'checkpoint.sctp_association_state': { + category: 'checkpoint', + description: 'The bad state you were trying to update to. ', + name: 'checkpoint.sctp_association_state', + type: 'keyword', + }, + 'checkpoint.tcp_packet_out_of_state': { + category: 'checkpoint', + description: 'State violation. ', + name: 'checkpoint.tcp_packet_out_of_state', + type: 'keyword', + }, + 'checkpoint.connectivity_level': { + category: 'checkpoint', + description: 'Log for a new connection in wire mode. ', + name: 'checkpoint.connectivity_level', + type: 'keyword', + }, + 'checkpoint.ip_option': { + category: 'checkpoint', + description: 'IP option that was dropped. ', + name: 'checkpoint.ip_option', + type: 'integer', + }, + 'checkpoint.tcp_state': { + category: 'checkpoint', + description: 'Log reinting a tcp state change. ', + name: 'checkpoint.tcp_state', + type: 'keyword', + }, + 'checkpoint.expire_time': { + category: 'checkpoint', + description: 'Connection closing time. ', + name: 'checkpoint.expire_time', + type: 'keyword', + }, + 'checkpoint.rpc_prog': { + category: 'checkpoint', + description: 'Log for new RPC state - prog values. ', + name: 'checkpoint.rpc_prog', + type: 'integer', + }, + 'checkpoint.dce-rpc_interface_uuid': { + category: 'checkpoint', + description: 'Log for new RPC state - UUID values ', + name: 'checkpoint.dce-rpc_interface_uuid', + type: 'keyword', + }, + 'checkpoint.elapsed': { + category: 'checkpoint', + description: 'Time passed since start time. ', + name: 'checkpoint.elapsed', + type: 'keyword', + }, + 'checkpoint.icmp': { + category: 'checkpoint', + description: 'Number of packets, received by the client. ', + name: 'checkpoint.icmp', + type: 'keyword', + }, + 'checkpoint.capture_uuid': { + category: 'checkpoint', + description: 'UUID generated for the capture. Used when enabling the capture when logging. ', + name: 'checkpoint.capture_uuid', + type: 'keyword', + }, + 'checkpoint.diameter_app_ID': { + category: 'checkpoint', + description: 'The ID of diameter application. ', + name: 'checkpoint.diameter_app_ID', + type: 'integer', + }, + 'checkpoint.diameter_cmd_code': { + category: 'checkpoint', + description: 'Diameter not allowed application command id. ', + name: 'checkpoint.diameter_cmd_code', + type: 'integer', + }, + 'checkpoint.diameter_msg_type': { + category: 'checkpoint', + description: 'Diameter message type. ', + name: 'checkpoint.diameter_msg_type', + type: 'keyword', + }, + 'checkpoint.cp_message': { + category: 'checkpoint', + description: 'Used to log a general message. ', + name: 'checkpoint.cp_message', + type: 'integer', + }, + 'checkpoint.log_delay': { + category: 'checkpoint', + description: 'Time left before deleting template. ', + name: 'checkpoint.log_delay', + type: 'integer', + }, + 'checkpoint.attack_status': { + category: 'checkpoint', + description: 'In case of a malicious event on an endpoint computer, the status of the attack. ', + name: 'checkpoint.attack_status', + type: 'keyword', + }, + 'checkpoint.impacted_files': { + category: 'checkpoint', + description: + 'In case of an infection on an endpoint computer, the list of files that the malware impacted. ', + name: 'checkpoint.impacted_files', + type: 'keyword', + }, + 'checkpoint.remediated_files': { + category: 'checkpoint', + description: + 'In case of an infection and a successful cleaning of that infection, this is a list of remediated files on the computer. ', + name: 'checkpoint.remediated_files', + type: 'keyword', + }, + 'checkpoint.triggered_by': { + category: 'checkpoint', + description: + 'The name of the mechanism that triggered the Software Blade to enforce a protection. ', + name: 'checkpoint.triggered_by', + type: 'keyword', + }, + 'checkpoint.https_inspection_rule_id': { + category: 'checkpoint', + description: 'ID of the matched rule. ', + name: 'checkpoint.https_inspection_rule_id', + type: 'keyword', + }, + 'checkpoint.https_inspection_rule_name': { + category: 'checkpoint', + description: 'Name of the matched rule. ', + name: 'checkpoint.https_inspection_rule_name', + type: 'keyword', + }, + 'checkpoint.app_properties': { + category: 'checkpoint', + description: 'List of all found categories. ', + name: 'checkpoint.app_properties', + type: 'keyword', + }, + 'checkpoint.https_validation': { + category: 'checkpoint', + description: 'Precise error, describing HTTPS inspection failure. ', + name: 'checkpoint.https_validation', + type: 'keyword', + }, + 'checkpoint.https_inspection_action': { + category: 'checkpoint', + description: 'HTTPS inspection action (Inspect/Bypass/Error). ', + name: 'checkpoint.https_inspection_action', + type: 'keyword', + }, + 'checkpoint.icap_service_id': { + category: 'checkpoint', + description: 'Service ID, can work with multiple servers, treated as services. ', + name: 'checkpoint.icap_service_id', + type: 'integer', + }, + 'checkpoint.icap_server_name': { + category: 'checkpoint', + description: 'Server name. ', + name: 'checkpoint.icap_server_name', + type: 'keyword', + }, + 'checkpoint.internal_error': { + category: 'checkpoint', + description: 'Internal error, for troubleshooting ', + name: 'checkpoint.internal_error', + type: 'keyword', + }, + 'checkpoint.icap_more_info': { + category: 'checkpoint', + description: 'Free text for verdict. ', + name: 'checkpoint.icap_more_info', + type: 'integer', + }, + 'checkpoint.reply_status': { + category: 'checkpoint', + description: 'ICAP reply status code, e.g. 200 or 204. ', + name: 'checkpoint.reply_status', + type: 'integer', + }, + 'checkpoint.icap_server_service': { + category: 'checkpoint', + description: 'Service name, as given in the ICAP URI ', + name: 'checkpoint.icap_server_service', + type: 'keyword', + }, + 'checkpoint.mirror_and_decrypt_type': { + category: 'checkpoint', + description: + 'Information about decrypt and forward. Possible values: Mirror only, Decrypt and mirror, Partial mirroring (HTTPS inspection Bypass). ', + name: 'checkpoint.mirror_and_decrypt_type', + type: 'keyword', + }, + 'checkpoint.interface_name': { + category: 'checkpoint', + description: 'Designated interface for mirror And decrypt. ', + name: 'checkpoint.interface_name', + type: 'keyword', + }, + 'checkpoint.session_uid': { + category: 'checkpoint', + description: 'HTTP session-id. ', + name: 'checkpoint.session_uid', + type: 'keyword', + }, + 'checkpoint.broker_publisher': { + category: 'checkpoint', + description: 'IP address of the broker publisher who shared the session information. ', + name: 'checkpoint.broker_publisher', + type: 'ip', + }, + 'checkpoint.src_user_dn': { + category: 'checkpoint', + description: 'User distinguished name connected to source IP. ', + name: 'checkpoint.src_user_dn', + type: 'keyword', + }, + 'checkpoint.proxy_user_name': { + category: 'checkpoint', + description: 'User name connected to proxy IP. ', + name: 'checkpoint.proxy_user_name', + type: 'keyword', + }, + 'checkpoint.proxy_machine_name': { + category: 'checkpoint', + description: 'Machine name connected to proxy IP. ', + name: 'checkpoint.proxy_machine_name', + type: 'integer', + }, + 'checkpoint.proxy_user_dn': { + category: 'checkpoint', + description: 'User distinguished name connected to proxy IP. ', + name: 'checkpoint.proxy_user_dn', + type: 'keyword', + }, + 'checkpoint.query': { + category: 'checkpoint', + description: 'DNS query. ', + name: 'checkpoint.query', + type: 'keyword', + }, + 'checkpoint.dns_query': { + category: 'checkpoint', + description: 'DNS query. ', + name: 'checkpoint.dns_query', + type: 'keyword', + }, + 'checkpoint.inspection_item': { + category: 'checkpoint', + description: 'Blade element performed inspection. ', + name: 'checkpoint.inspection_item', + type: 'keyword', + }, + 'checkpoint.inspection_category': { + category: 'checkpoint', + description: 'Inspection category: protocol anomaly, signature etc. ', + name: 'checkpoint.inspection_category', + type: 'keyword', + }, + 'checkpoint.inspection_profile': { + category: 'checkpoint', + description: 'Profile which the activated protection belongs to. ', + name: 'checkpoint.inspection_profile', + type: 'keyword', + }, + 'checkpoint.summary': { + category: 'checkpoint', + description: 'Summary message of a non-compliant DNS traffic drops or detects. ', + name: 'checkpoint.summary', + type: 'keyword', + }, + 'checkpoint.question_rdata': { + category: 'checkpoint', + description: 'List of question records domains. ', + name: 'checkpoint.question_rdata', + type: 'keyword', + }, + 'checkpoint.answer_rdata': { + category: 'checkpoint', + description: 'List of answer resource records to the questioned domains. ', + name: 'checkpoint.answer_rdata', + type: 'keyword', + }, + 'checkpoint.authority_rdata': { + category: 'checkpoint', + description: 'List of authoritative servers. ', + name: 'checkpoint.authority_rdata', + type: 'keyword', + }, + 'checkpoint.additional_rdata': { + category: 'checkpoint', + description: 'List of additional resource records. ', + name: 'checkpoint.additional_rdata', + type: 'keyword', + }, + 'checkpoint.files_names': { + category: 'checkpoint', + description: 'List of files requested by FTP. ', + name: 'checkpoint.files_names', + type: 'keyword', + }, + 'checkpoint.ftp_user': { + category: 'checkpoint', + description: 'FTP username. ', + name: 'checkpoint.ftp_user', + type: 'keyword', + }, + 'checkpoint.mime_from': { + category: 'checkpoint', + description: "Sender's address. ", + name: 'checkpoint.mime_from', + type: 'keyword', + }, + 'checkpoint.mime_to': { + category: 'checkpoint', + description: 'List of receiver address. ', + name: 'checkpoint.mime_to', + type: 'keyword', + }, + 'checkpoint.bcc': { + category: 'checkpoint', + description: 'List of BCC addresses. ', + name: 'checkpoint.bcc', + type: 'keyword', + }, + 'checkpoint.content_type': { + category: 'checkpoint', + description: + 'Mail content type. Possible values: application/msword, text/html, image/gif etc. ', + name: 'checkpoint.content_type', + type: 'keyword', + }, + 'checkpoint.user_agent': { + category: 'checkpoint', + description: 'String identifying requesting software user agent. ', + name: 'checkpoint.user_agent', + type: 'keyword', + }, + 'checkpoint.referrer': { + category: 'checkpoint', + description: 'Referrer HTTP request header, previous web page address. ', + name: 'checkpoint.referrer', + type: 'keyword', + }, + 'checkpoint.http_location': { + category: 'checkpoint', + description: 'Response header, indicates the URL to redirect a page to. ', + name: 'checkpoint.http_location', + type: 'keyword', + }, + 'checkpoint.content_disposition': { + category: 'checkpoint', + description: 'Indicates how the content is expected to be displayed inline in the browser. ', + name: 'checkpoint.content_disposition', + type: 'keyword', + }, + 'checkpoint.via': { + category: 'checkpoint', + description: + 'Via header is added by proxies for tracking purposes to avoid sending reqests in loop. ', + name: 'checkpoint.via', + type: 'keyword', + }, + 'checkpoint.http_server': { + category: 'checkpoint', + description: + 'Server HTTP header value, contains information about the software used by the origin server, which handles the request. ', + name: 'checkpoint.http_server', + type: 'keyword', + }, + 'checkpoint.content_length': { + category: 'checkpoint', + description: 'Indicates the size of the entity-body of the HTTP header. ', + name: 'checkpoint.content_length', + type: 'keyword', + }, + 'checkpoint.authorization': { + category: 'checkpoint', + description: 'Authorization HTTP header value. ', + name: 'checkpoint.authorization', + type: 'keyword', + }, + 'checkpoint.http_host': { + category: 'checkpoint', + description: 'Domain name of the server that the HTTP request is sent to. ', + name: 'checkpoint.http_host', + type: 'keyword', + }, + 'checkpoint.inspection_settings_log': { + category: 'checkpoint', + description: 'Indicats that the log was released by inspection settings. ', + name: 'checkpoint.inspection_settings_log', + type: 'keyword', + }, + 'checkpoint.cvpn_resource': { + category: 'checkpoint', + description: 'Mobile Access application. ', + name: 'checkpoint.cvpn_resource', + type: 'keyword', + }, + 'checkpoint.cvpn_category': { + category: 'checkpoint', + description: 'Mobile Access application type. ', + name: 'checkpoint.cvpn_category', + type: 'keyword', + }, + 'checkpoint.url': { + category: 'checkpoint', + description: 'Translated URL. ', + name: 'checkpoint.url', + type: 'keyword', + }, + 'checkpoint.reject_id': { + category: 'checkpoint', + description: + 'A reject ID that corresponds to the one presented in the Mobile Access error page. ', + name: 'checkpoint.reject_id', + type: 'keyword', + }, + 'checkpoint.fs-proto': { + category: 'checkpoint', + description: 'The file share protocol used in mobile acess file share application. ', + name: 'checkpoint.fs-proto', + type: 'keyword', + }, + 'checkpoint.app_package': { + category: 'checkpoint', + description: 'Unique identifier of the application on the protected mobile device. ', + name: 'checkpoint.app_package', + type: 'keyword', + }, + 'checkpoint.appi_name': { + category: 'checkpoint', + description: 'Name of application downloaded on the protected mobile device. ', + name: 'checkpoint.appi_name', + type: 'keyword', + }, + 'checkpoint.app_repackaged': { + category: 'checkpoint', + description: + 'Indicates whether the original application was repackage not by the official developer. ', + name: 'checkpoint.app_repackaged', + type: 'keyword', + }, + 'checkpoint.app_sid_id': { + category: 'checkpoint', + description: 'Unique SHA identifier of a mobile application. ', + name: 'checkpoint.app_sid_id', + type: 'keyword', + }, + 'checkpoint.app_version': { + category: 'checkpoint', + description: 'Version of the application downloaded on the protected mobile device. ', + name: 'checkpoint.app_version', + type: 'keyword', + }, + 'checkpoint.developer_certificate_name': { + category: 'checkpoint', + description: + "Name of the developer's certificate that was used to sign the mobile application. ", + name: 'checkpoint.developer_certificate_name', + type: 'keyword', + }, + 'checkpoint.email_message_id': { + category: 'checkpoint', + description: 'Email session id (uniqe ID of the mail). ', + name: 'checkpoint.email_message_id', + type: 'keyword', + }, + 'checkpoint.email_queue_id': { + category: 'checkpoint', + description: 'Postfix email queue id. ', + name: 'checkpoint.email_queue_id', + type: 'keyword', + }, + 'checkpoint.email_queue_name': { + category: 'checkpoint', + description: 'Postfix email queue name. ', + name: 'checkpoint.email_queue_name', + type: 'keyword', + }, + 'checkpoint.file_name': { + category: 'checkpoint', + description: 'Malicious file name. ', + name: 'checkpoint.file_name', + type: 'keyword', + }, + 'checkpoint.failure_reason': { + category: 'checkpoint', + description: 'MTA failure description. ', + name: 'checkpoint.failure_reason', + type: 'keyword', + }, + 'checkpoint.email_headers': { + category: 'checkpoint', + description: 'String containing all the email headers. ', + name: 'checkpoint.email_headers', + type: 'keyword', + }, + 'checkpoint.arrival_time': { + category: 'checkpoint', + description: 'Email arrival timestamp. ', + name: 'checkpoint.arrival_time', + type: 'keyword', + }, + 'checkpoint.email_status': { + category: 'checkpoint', + description: + "Describes the email's state. Possible options: delivered, deferred, skipped, bounced, hold, new, scan_started, scan_ended ", + name: 'checkpoint.email_status', + type: 'keyword', + }, + 'checkpoint.status_update': { + category: 'checkpoint', + description: 'Last time log was updated. ', + name: 'checkpoint.status_update', + type: 'keyword', + }, + 'checkpoint.delivery_time': { + category: 'checkpoint', + description: 'Timestamp of when email was delivered (MTA finished handling the email. ', + name: 'checkpoint.delivery_time', + type: 'keyword', + }, + 'checkpoint.links_num': { + category: 'checkpoint', + description: 'Number of links in the mail. ', + name: 'checkpoint.links_num', + type: 'integer', + }, + 'checkpoint.attachments_num': { + category: 'checkpoint', + description: 'Number of attachments in the mail. ', + name: 'checkpoint.attachments_num', + type: 'integer', + }, + 'checkpoint.email_content': { + category: 'checkpoint', + description: + 'Mail contents. Possible options: attachments/links & attachments/links/text only. ', + name: 'checkpoint.email_content', + type: 'keyword', + }, + 'checkpoint.allocated_ports': { + category: 'checkpoint', + description: 'Amount of allocated ports. ', + name: 'checkpoint.allocated_ports', + type: 'integer', + }, + 'checkpoint.capacity': { + category: 'checkpoint', + description: 'Capacity of the ports. ', + name: 'checkpoint.capacity', + type: 'integer', + }, + 'checkpoint.ports_usage': { + category: 'checkpoint', + description: 'Percentage of allocated ports. ', + name: 'checkpoint.ports_usage', + type: 'integer', + }, + 'checkpoint.nat_exhausted_pool': { + category: 'checkpoint', + description: '4-tuple of an exhausted pool. ', + name: 'checkpoint.nat_exhausted_pool', + type: 'keyword', + }, + 'checkpoint.nat_rulenum': { + category: 'checkpoint', + description: 'NAT rulebase first matched rule. ', + name: 'checkpoint.nat_rulenum', + type: 'integer', + }, + 'checkpoint.nat_addtnl_rulenum': { + category: 'checkpoint', + description: + 'When matching 2 automatic rules , second rule match will be shown otherwise field will be 0. ', + name: 'checkpoint.nat_addtnl_rulenum', + type: 'integer', + }, + 'checkpoint.message_info': { + category: 'checkpoint', + description: 'Used for information messages, for example:NAT connection has ended. ', + name: 'checkpoint.message_info', + type: 'keyword', + }, + 'checkpoint.nat46': { + category: 'checkpoint', + description: 'NAT 46 status, in most cases "enabled". ', + name: 'checkpoint.nat46', + type: 'keyword', + }, + 'checkpoint.end_time': { + category: 'checkpoint', + description: 'TCP connection end time. ', + name: 'checkpoint.end_time', + type: 'keyword', + }, + 'checkpoint.tcp_end_reason': { + category: 'checkpoint', + description: 'Reason for TCP connection closure. ', + name: 'checkpoint.tcp_end_reason', + type: 'keyword', + }, + 'checkpoint.cgnet': { + category: 'checkpoint', + description: 'Describes NAT allocation for specific subscriber. ', + name: 'checkpoint.cgnet', + type: 'keyword', + }, + 'checkpoint.subscriber': { + category: 'checkpoint', + description: 'Source IP before CGNAT. ', + name: 'checkpoint.subscriber', + type: 'ip', + }, + 'checkpoint.hide_ip': { + category: 'checkpoint', + description: 'Source IP which will be used after CGNAT. ', + name: 'checkpoint.hide_ip', + type: 'ip', + }, + 'checkpoint.int_start': { + category: 'checkpoint', + description: 'Subscriber start int which will be used for NAT. ', + name: 'checkpoint.int_start', + type: 'integer', + }, + 'checkpoint.int_end': { + category: 'checkpoint', + description: 'Subscriber end int which will be used for NAT. ', + name: 'checkpoint.int_end', + type: 'integer', + }, + 'checkpoint.packet_amount': { + category: 'checkpoint', + description: 'Amount of packets dropped. ', + name: 'checkpoint.packet_amount', + type: 'integer', + }, + 'checkpoint.monitor_reason': { + category: 'checkpoint', + description: 'Aggregated logs of monitored packets. ', + name: 'checkpoint.monitor_reason', + type: 'keyword', + }, + 'checkpoint.drops_amount': { + category: 'checkpoint', + description: 'Amount of multicast packets dropped. ', + name: 'checkpoint.drops_amount', + type: 'integer', + }, + 'checkpoint.securexl_message': { + category: 'checkpoint', + description: + 'Two options for a SecureXL message: 1. Missed accounting records after heavy load on logging system. 2. FW log message regarding a packet drop. ', + name: 'checkpoint.securexl_message', + type: 'keyword', + }, + 'checkpoint.conns_amount': { + category: 'checkpoint', + description: 'Connections amount of aggregated log info. ', + name: 'checkpoint.conns_amount', + type: 'integer', + }, + 'checkpoint.scope': { + category: 'checkpoint', + description: 'IP related to the attack. ', + name: 'checkpoint.scope', + type: 'keyword', + }, + 'checkpoint.analyzed_on': { + category: 'checkpoint', + description: 'Check Point ThreatCloud / emulator name. ', + name: 'checkpoint.analyzed_on', + type: 'keyword', + }, + 'checkpoint.detected_on': { + category: 'checkpoint', + description: 'System and applications version the file was emulated on. ', + name: 'checkpoint.detected_on', + type: 'keyword', + }, + 'checkpoint.dropped_file_name': { + category: 'checkpoint', + description: 'List of names dropped from the original file. ', + name: 'checkpoint.dropped_file_name', + type: 'keyword', + }, + 'checkpoint.dropped_file_type': { + category: 'checkpoint', + description: 'List of file types dropped from the original file. ', + name: 'checkpoint.dropped_file_type', + type: 'keyword', + }, + 'checkpoint.dropped_file_hash': { + category: 'checkpoint', + description: 'List of file hashes dropped from the original file. ', + name: 'checkpoint.dropped_file_hash', + type: 'keyword', + }, + 'checkpoint.dropped_file_verdict': { + category: 'checkpoint', + description: 'List of file verdics dropped from the original file. ', + name: 'checkpoint.dropped_file_verdict', + type: 'keyword', + }, + 'checkpoint.emulated_on': { + category: 'checkpoint', + description: 'Images the files were emulated on. ', + name: 'checkpoint.emulated_on', + type: 'keyword', + }, + 'checkpoint.extracted_file_type': { + category: 'checkpoint', + description: 'Types of extracted files in case of an archive. ', + name: 'checkpoint.extracted_file_type', + type: 'keyword', + }, + 'checkpoint.extracted_file_names': { + category: 'checkpoint', + description: 'Names of extracted files in case of an archive. ', + name: 'checkpoint.extracted_file_names', + type: 'keyword', + }, + 'checkpoint.extracted_file_hash': { + category: 'checkpoint', + description: 'Archive hash in case of extracted files. ', + name: 'checkpoint.extracted_file_hash', + type: 'keyword', + }, + 'checkpoint.extracted_file_verdict': { + category: 'checkpoint', + description: 'Verdict of extracted files in case of an archive. ', + name: 'checkpoint.extracted_file_verdict', + type: 'keyword', + }, + 'checkpoint.extracted_file_uid': { + category: 'checkpoint', + description: 'UID of extracted files in case of an archive. ', + name: 'checkpoint.extracted_file_uid', + type: 'keyword', + }, + 'checkpoint.mitre_initial_access': { + category: 'checkpoint', + description: 'The adversary is trying to break into your network. ', + name: 'checkpoint.mitre_initial_access', + type: 'keyword', + }, + 'checkpoint.mitre_execution': { + category: 'checkpoint', + description: 'The adversary is trying to run malicious code. ', + name: 'checkpoint.mitre_execution', + type: 'keyword', + }, + 'checkpoint.mitre_persistence': { + category: 'checkpoint', + description: 'The adversary is trying to maintain his foothold. ', + name: 'checkpoint.mitre_persistence', + type: 'keyword', + }, + 'checkpoint.mitre_privilege_escalation': { + category: 'checkpoint', + description: 'The adversary is trying to gain higher-level permissions. ', + name: 'checkpoint.mitre_privilege_escalation', + type: 'keyword', + }, + 'checkpoint.mitre_defense_evasion': { + category: 'checkpoint', + description: 'The adversary is trying to avoid being detected. ', + name: 'checkpoint.mitre_defense_evasion', + type: 'keyword', + }, + 'checkpoint.mitre_credential_access': { + category: 'checkpoint', + description: 'The adversary is trying to steal account names and passwords. ', + name: 'checkpoint.mitre_credential_access', + type: 'keyword', + }, + 'checkpoint.mitre_discovery': { + category: 'checkpoint', + description: 'The adversary is trying to expose information about your environment. ', + name: 'checkpoint.mitre_discovery', + type: 'keyword', + }, + 'checkpoint.mitre_lateral_movement': { + category: 'checkpoint', + description: 'The adversary is trying to explore your environment. ', + name: 'checkpoint.mitre_lateral_movement', + type: 'keyword', + }, + 'checkpoint.mitre_collection': { + category: 'checkpoint', + description: 'The adversary is trying to collect data of interest to achieve his goal. ', + name: 'checkpoint.mitre_collection', + type: 'keyword', + }, + 'checkpoint.mitre_command_and_control': { + category: 'checkpoint', + description: + 'The adversary is trying to communicate with compromised systems in order to control them. ', + name: 'checkpoint.mitre_command_and_control', + type: 'keyword', + }, + 'checkpoint.mitre_exfiltration': { + category: 'checkpoint', + description: 'The adversary is trying to steal data. ', + name: 'checkpoint.mitre_exfiltration', + type: 'keyword', + }, + 'checkpoint.mitre_impact': { + category: 'checkpoint', + description: + 'The adversary is trying to manipulate, interrupt, or destroy your systems and data. ', + name: 'checkpoint.mitre_impact', + type: 'keyword', + }, + 'checkpoint.parent_file_hash': { + category: 'checkpoint', + description: "Archive's hash in case of extracted files. ", + name: 'checkpoint.parent_file_hash', + type: 'keyword', + }, + 'checkpoint.parent_file_name': { + category: 'checkpoint', + description: "Archive's name in case of extracted files. ", + name: 'checkpoint.parent_file_name', + type: 'keyword', + }, + 'checkpoint.parent_file_uid': { + category: 'checkpoint', + description: "Archive's UID in case of extracted files. ", + name: 'checkpoint.parent_file_uid', + type: 'keyword', + }, + 'checkpoint.similiar_iocs': { + category: 'checkpoint', + description: 'Other IoCs similar to the ones found, related to the malicious file. ', + name: 'checkpoint.similiar_iocs', + type: 'keyword', + }, + 'checkpoint.similar_hashes': { + category: 'checkpoint', + description: 'Hashes found similar to the malicious file. ', + name: 'checkpoint.similar_hashes', + type: 'keyword', + }, + 'checkpoint.similar_strings': { + category: 'checkpoint', + description: 'Strings found similar to the malicious file. ', + name: 'checkpoint.similar_strings', + type: 'keyword', + }, + 'checkpoint.similar_communication': { + category: 'checkpoint', + description: 'Network action found similar to the malicious file. ', + name: 'checkpoint.similar_communication', + type: 'keyword', + }, + 'checkpoint.te_verdict_determined_by': { + category: 'checkpoint', + description: 'Emulators determined file verdict. ', + name: 'checkpoint.te_verdict_determined_by', + type: 'keyword', + }, + 'checkpoint.packet_capture_unique_id': { + category: 'checkpoint', + description: 'Identifier of the packet capture files. ', + name: 'checkpoint.packet_capture_unique_id', + type: 'keyword', + }, + 'checkpoint.total_attachments': { + category: 'checkpoint', + description: 'The number of attachments in an email. ', + name: 'checkpoint.total_attachments', + type: 'integer', + }, + 'checkpoint.additional_info': { + category: 'checkpoint', + description: 'ID of original file/mail which are sent by admin. ', + name: 'checkpoint.additional_info', + type: 'keyword', + }, + 'checkpoint.content_risk': { + category: 'checkpoint', + description: 'File risk. ', + name: 'checkpoint.content_risk', + type: 'integer', + }, + 'checkpoint.operation': { + category: 'checkpoint', + description: 'Operation made by Threat Extraction. ', + name: 'checkpoint.operation', + type: 'keyword', + }, + 'checkpoint.scrubbed_content': { + category: 'checkpoint', + description: 'Active content that was found. ', + name: 'checkpoint.scrubbed_content', + type: 'keyword', + }, + 'checkpoint.scrub_time': { + category: 'checkpoint', + description: 'Extraction process duration. ', + name: 'checkpoint.scrub_time', + type: 'keyword', + }, + 'checkpoint.scrub_download_time': { + category: 'checkpoint', + description: 'File download time from resource. ', + name: 'checkpoint.scrub_download_time', + type: 'keyword', + }, + 'checkpoint.scrub_total_time': { + category: 'checkpoint', + description: 'Threat extraction total file handling time. ', + name: 'checkpoint.scrub_total_time', + type: 'keyword', + }, + 'checkpoint.scrub_activity': { + category: 'checkpoint', + description: 'The result of the extraction ', + name: 'checkpoint.scrub_activity', + type: 'keyword', + }, + 'checkpoint.watermark': { + category: 'checkpoint', + description: 'Reports whether watermark is added to the cleaned file. ', + name: 'checkpoint.watermark', + type: 'keyword', + }, + 'checkpoint.source_object': { + category: 'checkpoint', + description: 'Matched object name on source column. ', + name: 'checkpoint.source_object', + type: 'integer', + }, + 'checkpoint.destination_object': { + category: 'checkpoint', + description: 'Matched object name on destination column. ', + name: 'checkpoint.destination_object', + type: 'keyword', + }, + 'checkpoint.drop_reason': { + category: 'checkpoint', + description: 'Drop reason description. ', + name: 'checkpoint.drop_reason', + type: 'keyword', + }, + 'checkpoint.hit': { + category: 'checkpoint', + description: 'Number of hits on a rule. ', + name: 'checkpoint.hit', + type: 'integer', + }, + 'checkpoint.rulebase_id': { + category: 'checkpoint', + description: 'Layer number. ', + name: 'checkpoint.rulebase_id', + type: 'integer', + }, + 'checkpoint.first_hit_time': { + category: 'checkpoint', + description: 'First hit time in current interval. ', + name: 'checkpoint.first_hit_time', + type: 'integer', + }, + 'checkpoint.last_hit_time': { + category: 'checkpoint', + description: 'Last hit time in current interval. ', + name: 'checkpoint.last_hit_time', + type: 'integer', + }, + 'checkpoint.rematch_info': { + category: 'checkpoint', + description: + 'Information sent when old connections cannot be matched during policy installation. ', + name: 'checkpoint.rematch_info', + type: 'keyword', + }, + 'checkpoint.last_rematch_time': { + category: 'checkpoint', + description: 'Connection rematched time. ', + name: 'checkpoint.last_rematch_time', + type: 'keyword', + }, + 'checkpoint.action_reason': { + category: 'checkpoint', + description: 'Connection drop reason. ', + name: 'checkpoint.action_reason', + type: 'integer', + }, + 'checkpoint.c_bytes': { + category: 'checkpoint', + description: 'Boolean value indicates whether bytes sent from the client side are used. ', + name: 'checkpoint.c_bytes', + type: 'integer', + }, + 'checkpoint.context_num': { + category: 'checkpoint', + description: 'Serial number of the log for a specific connection. ', + name: 'checkpoint.context_num', + type: 'integer', + }, + 'checkpoint.match_id': { + category: 'checkpoint', + description: 'Private key of the rule ', + name: 'checkpoint.match_id', + type: 'integer', + }, + 'checkpoint.alert': { + category: 'checkpoint', + description: 'Alert level of matched rule (for connection logs). ', + name: 'checkpoint.alert', + type: 'keyword', + }, + 'checkpoint.parent_rule': { + category: 'checkpoint', + description: 'Parent rule number, in case of inline layer. ', + name: 'checkpoint.parent_rule', + type: 'integer', + }, + 'checkpoint.match_fk': { + category: 'checkpoint', + description: 'Rule number. ', + name: 'checkpoint.match_fk', + type: 'integer', + }, + 'checkpoint.dropped_outgoing': { + category: 'checkpoint', + description: 'Number of outgoing bytes dropped when using UP-limit feature. ', + name: 'checkpoint.dropped_outgoing', + type: 'integer', + }, + 'checkpoint.dropped_incoming': { + category: 'checkpoint', + description: 'Number of incoming bytes dropped when using UP-limit feature. ', + name: 'checkpoint.dropped_incoming', + type: 'integer', + }, + 'checkpoint.media_type': { + category: 'checkpoint', + description: 'Media used (audio, video, etc.) ', + name: 'checkpoint.media_type', + type: 'keyword', + }, + 'checkpoint.sip_reason': { + category: 'checkpoint', + description: "Explains why 'source_ip' isn't allowed to redirect (handover). ", + name: 'checkpoint.sip_reason', + type: 'keyword', + }, + 'checkpoint.voip_method': { + category: 'checkpoint', + description: 'Registration request. ', + name: 'checkpoint.voip_method', + type: 'keyword', + }, + 'checkpoint.registered_ip-phones': { + category: 'checkpoint', + description: 'Registered IP-Phones. ', + name: 'checkpoint.registered_ip-phones', + type: 'keyword', + }, + 'checkpoint.voip_reg_user_type': { + category: 'checkpoint', + description: 'Registered IP-Phone type. ', + name: 'checkpoint.voip_reg_user_type', + type: 'keyword', + }, + 'checkpoint.voip_call_id': { + category: 'checkpoint', + description: 'Call-ID. ', + name: 'checkpoint.voip_call_id', + type: 'keyword', + }, + 'checkpoint.voip_reg_int': { + category: 'checkpoint', + description: 'Registration port. ', + name: 'checkpoint.voip_reg_int', + type: 'integer', + }, + 'checkpoint.voip_reg_ipp': { + category: 'checkpoint', + description: 'Registration IP protocol. ', + name: 'checkpoint.voip_reg_ipp', + type: 'integer', + }, + 'checkpoint.voip_reg_period': { + category: 'checkpoint', + description: 'Registration period. ', + name: 'checkpoint.voip_reg_period', + type: 'integer', + }, + 'checkpoint.src_phone_number': { + category: 'checkpoint', + description: 'Source IP-Phone. ', + name: 'checkpoint.src_phone_number', + type: 'keyword', + }, + 'checkpoint.voip_from_user_type': { + category: 'checkpoint', + description: 'Source IP-Phone type. ', + name: 'checkpoint.voip_from_user_type', + type: 'keyword', + }, + 'checkpoint.voip_to_user_type': { + category: 'checkpoint', + description: 'Destination IP-Phone type. ', + name: 'checkpoint.voip_to_user_type', + type: 'keyword', + }, + 'checkpoint.voip_call_dir': { + category: 'checkpoint', + description: 'Call direction: in/out. ', + name: 'checkpoint.voip_call_dir', + type: 'keyword', + }, + 'checkpoint.voip_call_state': { + category: 'checkpoint', + description: 'Call state. Possible values: in/out. ', + name: 'checkpoint.voip_call_state', + type: 'keyword', + }, + 'checkpoint.voip_call_term_time': { + category: 'checkpoint', + description: 'Call termination time stamp. ', + name: 'checkpoint.voip_call_term_time', + type: 'keyword', + }, + 'checkpoint.voip_duration': { + category: 'checkpoint', + description: 'Call duration (seconds). ', + name: 'checkpoint.voip_duration', + type: 'keyword', + }, + 'checkpoint.voip_media_port': { + category: 'checkpoint', + description: 'Media int. ', + name: 'checkpoint.voip_media_port', + type: 'keyword', + }, + 'checkpoint.voip_media_ipp': { + category: 'checkpoint', + description: 'Media IP protocol. ', + name: 'checkpoint.voip_media_ipp', + type: 'keyword', + }, + 'checkpoint.voip_est_codec': { + category: 'checkpoint', + description: 'Estimated codec. ', + name: 'checkpoint.voip_est_codec', + type: 'keyword', + }, + 'checkpoint.voip_exp': { + category: 'checkpoint', + description: 'Expiration. ', + name: 'checkpoint.voip_exp', + type: 'integer', + }, + 'checkpoint.voip_attach_sz': { + category: 'checkpoint', + description: 'Attachment size. ', + name: 'checkpoint.voip_attach_sz', + type: 'integer', + }, + 'checkpoint.voip_attach_action_info': { + category: 'checkpoint', + description: 'Attachment action Info. ', + name: 'checkpoint.voip_attach_action_info', + type: 'keyword', + }, + 'checkpoint.voip_media_codec': { + category: 'checkpoint', + description: 'Estimated codec. ', + name: 'checkpoint.voip_media_codec', + type: 'keyword', + }, + 'checkpoint.voip_reject_reason': { + category: 'checkpoint', + description: 'Reject reason. ', + name: 'checkpoint.voip_reject_reason', + type: 'keyword', + }, + 'checkpoint.voip_reason_info': { + category: 'checkpoint', + description: 'Information. ', + name: 'checkpoint.voip_reason_info', + type: 'keyword', + }, + 'checkpoint.voip_config': { + category: 'checkpoint', + description: 'Configuration. ', + name: 'checkpoint.voip_config', + type: 'keyword', + }, + 'checkpoint.voip_reg_server': { + category: 'checkpoint', + description: 'Registrar server IP address. ', + name: 'checkpoint.voip_reg_server', + type: 'ip', + }, + 'checkpoint.scv_user': { + category: 'checkpoint', + description: 'Username whose packets are dropped on SCV. ', + name: 'checkpoint.scv_user', + type: 'keyword', + }, + 'checkpoint.scv_message_info': { + category: 'checkpoint', + description: 'Drop reason. ', + name: 'checkpoint.scv_message_info', + type: 'keyword', + }, + 'checkpoint.ppp': { + category: 'checkpoint', + description: 'Authentication status. ', + name: 'checkpoint.ppp', + type: 'keyword', + }, + 'checkpoint.scheme': { + category: 'checkpoint', + description: 'Describes the scheme used for the log. ', + name: 'checkpoint.scheme', + type: 'keyword', + }, + 'checkpoint.machine': { + category: 'checkpoint', + description: 'L2TP machine which triggered the log and the log refers to it. ', + name: 'checkpoint.machine', + type: 'keyword', + }, + 'checkpoint.vpn_feature_name': { + category: 'checkpoint', + description: 'L2TP /IKE / Link Selection. ', + name: 'checkpoint.vpn_feature_name', + type: 'keyword', + }, + 'checkpoint.reject_category': { + category: 'checkpoint', + description: 'Authentication failure reason. ', + name: 'checkpoint.reject_category', + type: 'keyword', + }, + 'checkpoint.peer_ip_probing_status_update': { + category: 'checkpoint', + description: 'IP address response status. ', + name: 'checkpoint.peer_ip_probing_status_update', + type: 'keyword', + }, + 'checkpoint.peer_ip': { + category: 'checkpoint', + description: 'IP address which the client connects to. ', + name: 'checkpoint.peer_ip', + type: 'keyword', + }, + 'checkpoint.link_probing_status_update': { + category: 'checkpoint', + description: 'IP address response status. ', + name: 'checkpoint.link_probing_status_update', + type: 'keyword', + }, + 'checkpoint.source_interface': { + category: 'checkpoint', + description: 'External Interface name for source interface or Null if not found. ', + name: 'checkpoint.source_interface', + type: 'keyword', + }, + 'checkpoint.next_hop_ip': { + category: 'checkpoint', + description: 'Next hop IP address. ', + name: 'checkpoint.next_hop_ip', + type: 'keyword', + }, + 'checkpoint.srckeyid': { + category: 'checkpoint', + description: 'Initiator Spi ID. ', + name: 'checkpoint.srckeyid', + type: 'keyword', + }, + 'checkpoint.dstkeyid': { + category: 'checkpoint', + description: 'Responder Spi ID. ', + name: 'checkpoint.dstkeyid', + type: 'keyword', + }, + 'checkpoint.encryption_failure': { + category: 'checkpoint', + description: 'Message indicating why the encryption failed. ', + name: 'checkpoint.encryption_failure', + type: 'keyword', + }, + 'checkpoint.ike_ids': { + category: 'checkpoint', + description: 'All QM ids. ', + name: 'checkpoint.ike_ids', + type: 'keyword', + }, + 'checkpoint.community': { + category: 'checkpoint', + description: 'Community name for the IPSec key and the use of the IKEv. ', + name: 'checkpoint.community', + type: 'keyword', + }, + 'checkpoint.ike': { + category: 'checkpoint', + description: 'IKEMode (PHASE1, PHASE2, etc..). ', + name: 'checkpoint.ike', + type: 'keyword', + }, + 'checkpoint.cookieI': { + category: 'checkpoint', + description: 'Initiator cookie. ', + name: 'checkpoint.cookieI', + type: 'keyword', + }, + 'checkpoint.cookieR': { + category: 'checkpoint', + description: 'Responder cookie. ', + name: 'checkpoint.cookieR', + type: 'keyword', + }, + 'checkpoint.msgid': { + category: 'checkpoint', + description: 'Message ID. ', + name: 'checkpoint.msgid', + type: 'keyword', + }, + 'checkpoint.methods': { + category: 'checkpoint', + description: 'IPSEc methods. ', + name: 'checkpoint.methods', + type: 'keyword', + }, + 'checkpoint.connection_uid': { + category: 'checkpoint', + description: 'Calculation of md5 of the IP and user name as UID. ', + name: 'checkpoint.connection_uid', + type: 'keyword', + }, + 'checkpoint.site_name': { + category: 'checkpoint', + description: 'Site name. ', + name: 'checkpoint.site_name', + type: 'keyword', + }, + 'checkpoint.esod_rule_name': { + category: 'checkpoint', + description: 'Unknown rule name. ', + name: 'checkpoint.esod_rule_name', + type: 'keyword', + }, + 'checkpoint.esod_rule_action': { + category: 'checkpoint', + description: 'Unknown rule action. ', + name: 'checkpoint.esod_rule_action', + type: 'keyword', + }, + 'checkpoint.esod_rule_type': { + category: 'checkpoint', + description: 'Unknown rule type. ', + name: 'checkpoint.esod_rule_type', + type: 'keyword', + }, + 'checkpoint.esod_noncompliance_reason': { + category: 'checkpoint', + description: 'Non-compliance reason. ', + name: 'checkpoint.esod_noncompliance_reason', + type: 'keyword', + }, + 'checkpoint.esod_associated_policies': { + category: 'checkpoint', + description: 'Associated policies. ', + name: 'checkpoint.esod_associated_policies', + type: 'keyword', + }, + 'checkpoint.spyware_type': { + category: 'checkpoint', + description: 'Spyware type. ', + name: 'checkpoint.spyware_type', + type: 'keyword', + }, + 'checkpoint.anti_virus_type': { + category: 'checkpoint', + description: 'Anti virus type. ', + name: 'checkpoint.anti_virus_type', + type: 'keyword', + }, + 'checkpoint.end_user_firewall_type': { + category: 'checkpoint', + description: 'End user firewall type. ', + name: 'checkpoint.end_user_firewall_type', + type: 'keyword', + }, + 'checkpoint.esod_scan_status': { + category: 'checkpoint', + description: 'Scan failed. ', + name: 'checkpoint.esod_scan_status', + type: 'keyword', + }, + 'checkpoint.esod_access_status': { + category: 'checkpoint', + description: 'Access denied. ', + name: 'checkpoint.esod_access_status', + type: 'keyword', + }, + 'checkpoint.client_type': { + category: 'checkpoint', + description: 'Endpoint Connect. ', + name: 'checkpoint.client_type', + type: 'keyword', + }, + 'checkpoint.precise_error': { + category: 'checkpoint', + description: 'HTTP parser error. ', + name: 'checkpoint.precise_error', + type: 'keyword', + }, + 'checkpoint.method': { + category: 'checkpoint', + description: 'HTTP method. ', + name: 'checkpoint.method', + type: 'keyword', + }, + 'checkpoint.trusted_domain': { + category: 'checkpoint', + description: 'In case of phishing event, the domain, which the attacker was impersonating. ', + name: 'checkpoint.trusted_domain', + type: 'keyword', + }, + 'cisco.asa.message_id': { + category: 'cisco', + description: 'The Cisco ASA message identifier. ', + name: 'cisco.asa.message_id', + type: 'keyword', + }, + 'cisco.asa.suffix': { + category: 'cisco', + description: 'Optional suffix after %ASA identifier. ', + example: 'session', + name: 'cisco.asa.suffix', + type: 'keyword', + }, + 'cisco.asa.source_interface': { + category: 'cisco', + description: 'Source interface for the flow or event. ', + name: 'cisco.asa.source_interface', + type: 'keyword', + }, + 'cisco.asa.destination_interface': { + category: 'cisco', + description: 'Destination interface for the flow or event. ', + name: 'cisco.asa.destination_interface', + type: 'keyword', + }, + 'cisco.asa.rule_name': { + category: 'cisco', + description: 'Name of the Access Control List rule that matched this event. ', + name: 'cisco.asa.rule_name', + type: 'keyword', + }, + 'cisco.asa.source_username': { + category: 'cisco', + description: 'Name of the user that is the source for this event. ', + name: 'cisco.asa.source_username', + type: 'keyword', + }, + 'cisco.asa.destination_username': { + category: 'cisco', + description: 'Name of the user that is the destination for this event. ', + name: 'cisco.asa.destination_username', + type: 'keyword', + }, + 'cisco.asa.mapped_source_ip': { + category: 'cisco', + description: 'The translated source IP address. ', + name: 'cisco.asa.mapped_source_ip', + type: 'ip', + }, + 'cisco.asa.mapped_source_host': { + category: 'cisco', + description: 'The translated source host. ', + name: 'cisco.asa.mapped_source_host', + type: 'keyword', + }, + 'cisco.asa.mapped_source_port': { + category: 'cisco', + description: 'The translated source port. ', + name: 'cisco.asa.mapped_source_port', + type: 'long', + }, + 'cisco.asa.mapped_destination_ip': { + category: 'cisco', + description: 'The translated destination IP address. ', + name: 'cisco.asa.mapped_destination_ip', + type: 'ip', + }, + 'cisco.asa.mapped_destination_host': { + category: 'cisco', + description: 'The translated destination host. ', + name: 'cisco.asa.mapped_destination_host', + type: 'keyword', + }, + 'cisco.asa.mapped_destination_port': { + category: 'cisco', + description: 'The translated destination port. ', + name: 'cisco.asa.mapped_destination_port', + type: 'long', + }, + 'cisco.asa.threat_level': { + category: 'cisco', + description: + 'Threat level for malware / botnet traffic. One of very-low, low, moderate, high or very-high. ', + name: 'cisco.asa.threat_level', + type: 'keyword', + }, + 'cisco.asa.threat_category': { + category: 'cisco', + description: + 'Category for the malware / botnet traffic. For example: virus, botnet, trojan, etc. ', + name: 'cisco.asa.threat_category', + type: 'keyword', + }, + 'cisco.asa.connection_id': { + category: 'cisco', + description: 'Unique identifier for a flow. ', + name: 'cisco.asa.connection_id', + type: 'keyword', + }, + 'cisco.asa.icmp_type': { + category: 'cisco', + description: 'ICMP type. ', + name: 'cisco.asa.icmp_type', + type: 'short', + }, + 'cisco.asa.icmp_code': { + category: 'cisco', + description: 'ICMP code. ', + name: 'cisco.asa.icmp_code', + type: 'short', + }, + 'cisco.asa.connection_type': { + category: 'cisco', + description: 'The VPN connection type ', + name: 'cisco.asa.connection_type', + type: 'keyword', + }, + 'cisco.asa.dap_records': { + category: 'cisco', + description: 'The assigned DAP records ', + name: 'cisco.asa.dap_records', + type: 'keyword', + }, + 'cisco.ftd.message_id': { + category: 'cisco', + description: 'The Cisco FTD message identifier. ', + name: 'cisco.ftd.message_id', + type: 'keyword', + }, + 'cisco.ftd.suffix': { + category: 'cisco', + description: 'Optional suffix after %FTD identifier. ', + example: 'session', + name: 'cisco.ftd.suffix', + type: 'keyword', + }, + 'cisco.ftd.source_interface': { + category: 'cisco', + description: 'Source interface for the flow or event. ', + name: 'cisco.ftd.source_interface', + type: 'keyword', + }, + 'cisco.ftd.destination_interface': { + category: 'cisco', + description: 'Destination interface for the flow or event. ', + name: 'cisco.ftd.destination_interface', + type: 'keyword', + }, + 'cisco.ftd.rule_name': { + category: 'cisco', + description: 'Name of the Access Control List rule that matched this event. ', + name: 'cisco.ftd.rule_name', + type: 'keyword', + }, + 'cisco.ftd.source_username': { + category: 'cisco', + description: 'Name of the user that is the source for this event. ', + name: 'cisco.ftd.source_username', + type: 'keyword', + }, + 'cisco.ftd.destination_username': { + category: 'cisco', + description: 'Name of the user that is the destination for this event. ', + name: 'cisco.ftd.destination_username', + type: 'keyword', + }, + 'cisco.ftd.mapped_source_ip': { + category: 'cisco', + description: 'The translated source IP address. Use ECS source.nat.ip. ', + name: 'cisco.ftd.mapped_source_ip', + type: 'ip', + }, + 'cisco.ftd.mapped_source_host': { + category: 'cisco', + description: 'The translated source host. ', + name: 'cisco.ftd.mapped_source_host', + type: 'keyword', + }, + 'cisco.ftd.mapped_source_port': { + category: 'cisco', + description: 'The translated source port. Use ECS source.nat.port. ', + name: 'cisco.ftd.mapped_source_port', + type: 'long', + }, + 'cisco.ftd.mapped_destination_ip': { + category: 'cisco', + description: 'The translated destination IP address. Use ECS destination.nat.ip. ', + name: 'cisco.ftd.mapped_destination_ip', + type: 'ip', + }, + 'cisco.ftd.mapped_destination_host': { + category: 'cisco', + description: 'The translated destination host. ', + name: 'cisco.ftd.mapped_destination_host', + type: 'keyword', + }, + 'cisco.ftd.mapped_destination_port': { + category: 'cisco', + description: 'The translated destination port. Use ECS destination.nat.port. ', + name: 'cisco.ftd.mapped_destination_port', + type: 'long', + }, + 'cisco.ftd.threat_level': { + category: 'cisco', + description: + 'Threat level for malware / botnet traffic. One of very-low, low, moderate, high or very-high. ', + name: 'cisco.ftd.threat_level', + type: 'keyword', + }, + 'cisco.ftd.threat_category': { + category: 'cisco', + description: + 'Category for the malware / botnet traffic. For example: virus, botnet, trojan, etc. ', + name: 'cisco.ftd.threat_category', + type: 'keyword', + }, + 'cisco.ftd.connection_id': { + category: 'cisco', + description: 'Unique identifier for a flow. ', + name: 'cisco.ftd.connection_id', + type: 'keyword', + }, + 'cisco.ftd.icmp_type': { + category: 'cisco', + description: 'ICMP type. ', + name: 'cisco.ftd.icmp_type', + type: 'short', + }, + 'cisco.ftd.icmp_code': { + category: 'cisco', + description: 'ICMP code. ', + name: 'cisco.ftd.icmp_code', + type: 'short', + }, + 'cisco.ftd.security': { + category: 'cisco', + description: 'Raw fields for Security Events.', + name: 'cisco.ftd.security', + type: 'object', + }, + 'cisco.ftd.connection_type': { + category: 'cisco', + description: 'The VPN connection type ', + name: 'cisco.ftd.connection_type', + type: 'keyword', + }, + 'cisco.ftd.dap_records': { + category: 'cisco', + description: 'The assigned DAP records ', + name: 'cisco.ftd.dap_records', + type: 'keyword', + }, + 'cisco.ios.access_list': { + category: 'cisco', + description: 'Name of the IP access list. ', + name: 'cisco.ios.access_list', + type: 'keyword', + }, + 'cisco.ios.facility': { + category: 'cisco', + description: + 'The facility to which the message refers (for example, SNMP, SYS, and so forth). A facility can be a hardware device, a protocol, or a module of the system software. It denotes the source or the cause of the system message. ', + example: 'SEC', + name: 'cisco.ios.facility', + type: 'keyword', + }, + 'coredns.id': { + category: 'coredns', + description: 'id of the DNS transaction ', + name: 'coredns.id', + type: 'keyword', + }, + 'coredns.query.size': { + category: 'coredns', + description: 'size of the DNS query ', + name: 'coredns.query.size', + type: 'integer', + format: 'bytes', + }, + 'coredns.query.class': { + category: 'coredns', + description: 'DNS query class ', + name: 'coredns.query.class', + type: 'keyword', + }, + 'coredns.query.name': { + category: 'coredns', + description: 'DNS query name ', + name: 'coredns.query.name', + type: 'keyword', + }, + 'coredns.query.type': { + category: 'coredns', + description: 'DNS query type ', + name: 'coredns.query.type', + type: 'keyword', + }, + 'coredns.response.code': { + category: 'coredns', + description: 'DNS response code ', + name: 'coredns.response.code', + type: 'keyword', + }, + 'coredns.response.flags': { + category: 'coredns', + description: 'DNS response flags ', + name: 'coredns.response.flags', + type: 'keyword', + }, + 'coredns.response.size': { + category: 'coredns', + description: 'size of the DNS response ', + name: 'coredns.response.size', + type: 'integer', + format: 'bytes', + }, + 'coredns.dnssec_ok': { + category: 'coredns', + description: 'dnssec flag ', + name: 'coredns.dnssec_ok', + type: 'boolean', + }, + 'crowdstrike.metadata.eventType': { + category: 'crowdstrike', + description: + 'DetectionSummaryEvent, FirewallMatchEvent, IncidentSummaryEvent, RemoteResponseSessionStartEvent, RemoteResponseSessionEndEvent, AuthActivityAuditEvent, or UserActivityAuditEvent ', + name: 'crowdstrike.metadata.eventType', + type: 'keyword', + }, + 'crowdstrike.metadata.eventCreationTime': { + category: 'crowdstrike', + description: 'The time this event occurred on the endpoint in UTC UNIX_MS format. ', + name: 'crowdstrike.metadata.eventCreationTime', + type: 'date', + }, + 'crowdstrike.metadata.offset': { + category: 'crowdstrike', + description: + 'Offset number that tracks the location of the event in stream. This is used to identify unique detection events. ', + name: 'crowdstrike.metadata.offset', + type: 'integer', + }, + 'crowdstrike.metadata.customerIDString': { + category: 'crowdstrike', + description: 'Customer identifier ', + name: 'crowdstrike.metadata.customerIDString', + type: 'keyword', + }, + 'crowdstrike.metadata.version': { + category: 'crowdstrike', + description: 'Schema version ', + name: 'crowdstrike.metadata.version', + type: 'keyword', + }, + 'crowdstrike.event.ProcessStartTime': { + category: 'crowdstrike', + description: 'The process start time in UTC UNIX_MS format. ', + name: 'crowdstrike.event.ProcessStartTime', + type: 'date', + }, + 'crowdstrike.event.ProcessEndTime': { + category: 'crowdstrike', + description: 'The process termination time in UTC UNIX_MS format. ', + name: 'crowdstrike.event.ProcessEndTime', + type: 'date', + }, + 'crowdstrike.event.ProcessId': { + category: 'crowdstrike', + description: 'Process ID related to the detection. ', + name: 'crowdstrike.event.ProcessId', + type: 'integer', + }, + 'crowdstrike.event.ParentProcessId': { + category: 'crowdstrike', + description: 'Parent process ID related to the detection. ', + name: 'crowdstrike.event.ParentProcessId', + type: 'integer', + }, + 'crowdstrike.event.ComputerName': { + category: 'crowdstrike', + description: 'Name of the computer where the detection occurred. ', + name: 'crowdstrike.event.ComputerName', + type: 'keyword', + }, + 'crowdstrike.event.UserName': { + category: 'crowdstrike', + description: 'User name associated with the detection. ', + name: 'crowdstrike.event.UserName', + type: 'keyword', + }, + 'crowdstrike.event.DetectName': { + category: 'crowdstrike', + description: 'Name of the detection. ', + name: 'crowdstrike.event.DetectName', + type: 'keyword', + }, + 'crowdstrike.event.DetectDescription': { + category: 'crowdstrike', + description: 'Description of the detection. ', + name: 'crowdstrike.event.DetectDescription', + type: 'keyword', + }, + 'crowdstrike.event.Severity': { + category: 'crowdstrike', + description: 'Severity score of the detection. ', + name: 'crowdstrike.event.Severity', + type: 'integer', + }, + 'crowdstrike.event.SeverityName': { + category: 'crowdstrike', + description: 'Severity score text. ', + name: 'crowdstrike.event.SeverityName', + type: 'keyword', + }, + 'crowdstrike.event.FileName': { + category: 'crowdstrike', + description: 'File name of the associated process for the detection. ', + name: 'crowdstrike.event.FileName', + type: 'keyword', + }, + 'crowdstrike.event.FilePath': { + category: 'crowdstrike', + description: 'Path of the executable associated with the detection. ', + name: 'crowdstrike.event.FilePath', + type: 'keyword', + }, + 'crowdstrike.event.CommandLine': { + category: 'crowdstrike', + description: 'Executable path with command line arguments. ', + name: 'crowdstrike.event.CommandLine', + type: 'keyword', + }, + 'crowdstrike.event.SHA1String': { + category: 'crowdstrike', + description: 'SHA1 sum of the executable associated with the detection. ', + name: 'crowdstrike.event.SHA1String', + type: 'keyword', + }, + 'crowdstrike.event.SHA256String': { + category: 'crowdstrike', + description: 'SHA256 sum of the executable associated with the detection. ', + name: 'crowdstrike.event.SHA256String', + type: 'keyword', + }, + 'crowdstrike.event.MD5String': { + category: 'crowdstrike', + description: 'MD5 sum of the executable associated with the detection. ', + name: 'crowdstrike.event.MD5String', + type: 'keyword', + }, + 'crowdstrike.event.MachineDomain': { + category: 'crowdstrike', + description: 'Domain for the machine associated with the detection. ', + name: 'crowdstrike.event.MachineDomain', + type: 'keyword', + }, + 'crowdstrike.event.FalconHostLink': { + category: 'crowdstrike', + description: 'URL to view the detection in Falcon. ', + name: 'crowdstrike.event.FalconHostLink', + type: 'keyword', + }, + 'crowdstrike.event.SensorId': { + category: 'crowdstrike', + description: 'Unique ID associated with the Falcon sensor. ', + name: 'crowdstrike.event.SensorId', + type: 'keyword', + }, + 'crowdstrike.event.DetectId': { + category: 'crowdstrike', + description: 'Unique ID associated with the detection. ', + name: 'crowdstrike.event.DetectId', + type: 'keyword', + }, + 'crowdstrike.event.LocalIP': { + category: 'crowdstrike', + description: 'IP address of the host associated with the detection. ', + name: 'crowdstrike.event.LocalIP', + type: 'keyword', + }, + 'crowdstrike.event.MACAddress': { + category: 'crowdstrike', + description: 'MAC address of the host associated with the detection. ', + name: 'crowdstrike.event.MACAddress', + type: 'keyword', + }, + 'crowdstrike.event.Tactic': { + category: 'crowdstrike', + description: 'MITRE tactic category of the detection. ', + name: 'crowdstrike.event.Tactic', + type: 'keyword', + }, + 'crowdstrike.event.Technique': { + category: 'crowdstrike', + description: 'MITRE technique category of the detection. ', + name: 'crowdstrike.event.Technique', + type: 'keyword', + }, + 'crowdstrike.event.Objective': { + category: 'crowdstrike', + description: 'Method of detection. ', + name: 'crowdstrike.event.Objective', + type: 'keyword', + }, + 'crowdstrike.event.PatternDispositionDescription': { + category: 'crowdstrike', + description: 'Action taken by Falcon. ', + name: 'crowdstrike.event.PatternDispositionDescription', + type: 'keyword', + }, + 'crowdstrike.event.PatternDispositionValue': { + category: 'crowdstrike', + description: 'Unique ID associated with action taken. ', + name: 'crowdstrike.event.PatternDispositionValue', + type: 'integer', + }, + 'crowdstrike.event.PatternDispositionFlags': { + category: 'crowdstrike', + description: 'Flags indicating actions taken. ', + name: 'crowdstrike.event.PatternDispositionFlags', + type: 'object', + }, + 'crowdstrike.event.State': { + category: 'crowdstrike', + description: 'Whether the incident summary is open and ongoing or closed. ', + name: 'crowdstrike.event.State', + type: 'keyword', + }, + 'crowdstrike.event.IncidentStartTime': { + category: 'crowdstrike', + description: 'Start time for the incident in UTC UNIX format. ', + name: 'crowdstrike.event.IncidentStartTime', + type: 'date', + }, + 'crowdstrike.event.IncidentEndTime': { + category: 'crowdstrike', + description: 'End time for the incident in UTC UNIX format. ', + name: 'crowdstrike.event.IncidentEndTime', + type: 'date', + }, + 'crowdstrike.event.FineScore': { + category: 'crowdstrike', + description: 'Score for incident. ', + name: 'crowdstrike.event.FineScore', + type: 'float', + }, + 'crowdstrike.event.UserId': { + category: 'crowdstrike', + description: 'Email address or user ID associated with the event. ', + name: 'crowdstrike.event.UserId', + type: 'keyword', + }, + 'crowdstrike.event.UserIp': { + category: 'crowdstrike', + description: 'IP address associated with the user. ', + name: 'crowdstrike.event.UserIp', + type: 'keyword', + }, + 'crowdstrike.event.OperationName': { + category: 'crowdstrike', + description: 'Event subtype. ', + name: 'crowdstrike.event.OperationName', + type: 'keyword', + }, + 'crowdstrike.event.ServiceName': { + category: 'crowdstrike', + description: 'Service associated with this event. ', + name: 'crowdstrike.event.ServiceName', + type: 'keyword', + }, + 'crowdstrike.event.Success': { + category: 'crowdstrike', + description: 'Indicator of whether or not this event was successful. ', + name: 'crowdstrike.event.Success', + type: 'boolean', + }, + 'crowdstrike.event.UTCTimestamp': { + category: 'crowdstrike', + description: 'Timestamp associated with this event in UTC UNIX format. ', + name: 'crowdstrike.event.UTCTimestamp', + type: 'date', + }, + 'crowdstrike.event.AuditKeyValues': { + category: 'crowdstrike', + description: 'Fields that were changed in this event. ', + name: 'crowdstrike.event.AuditKeyValues', + type: 'nested', + }, + 'crowdstrike.event.ExecutablesWritten': { + category: 'crowdstrike', + description: 'Detected executables written to disk by a process. ', + name: 'crowdstrike.event.ExecutablesWritten', + type: 'nested', + }, + 'crowdstrike.event.SessionId': { + category: 'crowdstrike', + description: 'Session ID of the remote response session. ', + name: 'crowdstrike.event.SessionId', + type: 'keyword', + }, + 'crowdstrike.event.HostnameField': { + category: 'crowdstrike', + description: 'Host name of the machine for the remote session. ', + name: 'crowdstrike.event.HostnameField', + type: 'keyword', + }, + 'crowdstrike.event.StartTimestamp': { + category: 'crowdstrike', + description: 'Start time for the remote session in UTC UNIX format. ', + name: 'crowdstrike.event.StartTimestamp', + type: 'date', + }, + 'crowdstrike.event.EndTimestamp': { + category: 'crowdstrike', + description: 'End time for the remote session in UTC UNIX format. ', + name: 'crowdstrike.event.EndTimestamp', + type: 'date', + }, + 'crowdstrike.event.LateralMovement': { + category: 'crowdstrike', + description: 'Lateral movement field for incident. ', + name: 'crowdstrike.event.LateralMovement', + type: 'long', + }, + 'crowdstrike.event.ParentImageFileName': { + category: 'crowdstrike', + description: 'Path to the parent process. ', + name: 'crowdstrike.event.ParentImageFileName', + type: 'keyword', + }, + 'crowdstrike.event.ParentCommandLine': { + category: 'crowdstrike', + description: 'Parent process command line arguments. ', + name: 'crowdstrike.event.ParentCommandLine', + type: 'keyword', + }, + 'crowdstrike.event.GrandparentImageFileName': { + category: 'crowdstrike', + description: 'Path to the grandparent process. ', + name: 'crowdstrike.event.GrandparentImageFileName', + type: 'keyword', + }, + 'crowdstrike.event.GrandparentCommandLine': { + category: 'crowdstrike', + description: 'Grandparent process command line arguments. ', + name: 'crowdstrike.event.GrandparentCommandLine', + type: 'keyword', + }, + 'crowdstrike.event.IOCType': { + category: 'crowdstrike', + description: 'CrowdStrike type for indicator of compromise. ', + name: 'crowdstrike.event.IOCType', + type: 'keyword', + }, + 'crowdstrike.event.IOCValue': { + category: 'crowdstrike', + description: 'CrowdStrike value for indicator of compromise. ', + name: 'crowdstrike.event.IOCValue', + type: 'keyword', + }, + 'crowdstrike.event.CustomerId': { + category: 'crowdstrike', + description: 'Customer identifier. ', + name: 'crowdstrike.event.CustomerId', + type: 'keyword', + }, + 'crowdstrike.event.DeviceId': { + category: 'crowdstrike', + description: 'Device on which the event occurred. ', + name: 'crowdstrike.event.DeviceId', + type: 'keyword', + }, + 'crowdstrike.event.Ipv': { + category: 'crowdstrike', + description: 'Protocol for network request. ', + name: 'crowdstrike.event.Ipv', + type: 'keyword', + }, + 'crowdstrike.event.ConnectionDirection': { + category: 'crowdstrike', + description: 'Direction for network connection. ', + name: 'crowdstrike.event.ConnectionDirection', + type: 'keyword', + }, + 'crowdstrike.event.EventType': { + category: 'crowdstrike', + description: 'CrowdStrike provided event type. ', + name: 'crowdstrike.event.EventType', + type: 'keyword', + }, + 'crowdstrike.event.HostName': { + category: 'crowdstrike', + description: 'Host name of the local machine. ', + name: 'crowdstrike.event.HostName', + type: 'keyword', + }, + 'crowdstrike.event.ICMPCode': { + category: 'crowdstrike', + description: 'RFC2780 ICMP Code field. ', + name: 'crowdstrike.event.ICMPCode', + type: 'keyword', + }, + 'crowdstrike.event.ICMPType': { + category: 'crowdstrike', + description: 'RFC2780 ICMP Type field. ', + name: 'crowdstrike.event.ICMPType', + type: 'keyword', + }, + 'crowdstrike.event.ImageFileName': { + category: 'crowdstrike', + description: 'File name of the associated process for the detection. ', + name: 'crowdstrike.event.ImageFileName', + type: 'keyword', + }, + 'crowdstrike.event.PID': { + category: 'crowdstrike', + description: 'Associated process id for the detection. ', + name: 'crowdstrike.event.PID', + type: 'long', + }, + 'crowdstrike.event.LocalAddress': { + category: 'crowdstrike', + description: 'IP address of local machine. ', + name: 'crowdstrike.event.LocalAddress', + type: 'ip', + }, + 'crowdstrike.event.LocalPort': { + category: 'crowdstrike', + description: 'Port of local machine. ', + name: 'crowdstrike.event.LocalPort', + type: 'long', + }, + 'crowdstrike.event.RemoteAddress': { + category: 'crowdstrike', + description: 'IP address of remote machine. ', + name: 'crowdstrike.event.RemoteAddress', + type: 'ip', + }, + 'crowdstrike.event.RemotePort': { + category: 'crowdstrike', + description: 'Port of remote machine. ', + name: 'crowdstrike.event.RemotePort', + type: 'long', + }, + 'crowdstrike.event.RuleAction': { + category: 'crowdstrike', + description: 'Firewall rule action. ', + name: 'crowdstrike.event.RuleAction', + type: 'keyword', + }, + 'crowdstrike.event.RuleDescription': { + category: 'crowdstrike', + description: 'Firewall rule description. ', + name: 'crowdstrike.event.RuleDescription', + type: 'keyword', + }, + 'crowdstrike.event.RuleFamilyID': { + category: 'crowdstrike', + description: 'Firewall rule family id. ', + name: 'crowdstrike.event.RuleFamilyID', + type: 'keyword', + }, + 'crowdstrike.event.RuleGroupName': { + category: 'crowdstrike', + description: 'Firewall rule group name. ', + name: 'crowdstrike.event.RuleGroupName', + type: 'keyword', + }, + 'crowdstrike.event.RuleName': { + category: 'crowdstrike', + description: 'Firewall rule name. ', + name: 'crowdstrike.event.RuleName', + type: 'keyword', + }, + 'crowdstrike.event.RuleId': { + category: 'crowdstrike', + description: 'Firewall rule id. ', + name: 'crowdstrike.event.RuleId', + type: 'keyword', + }, + 'crowdstrike.event.MatchCount': { + category: 'crowdstrike', + description: 'Number of firewall rule matches. ', + name: 'crowdstrike.event.MatchCount', + type: 'long', + }, + 'crowdstrike.event.MatchCountSinceLastReport': { + category: 'crowdstrike', + description: 'Number of firewall rule matches since the last report. ', + name: 'crowdstrike.event.MatchCountSinceLastReport', + type: 'long', + }, + 'crowdstrike.event.Timestamp': { + category: 'crowdstrike', + description: 'Firewall rule triggered timestamp. ', + name: 'crowdstrike.event.Timestamp', + type: 'date', + }, + 'crowdstrike.event.Flags.Audit': { + category: 'crowdstrike', + description: 'CrowdStrike audit flag. ', + name: 'crowdstrike.event.Flags.Audit', + type: 'boolean', + }, + 'crowdstrike.event.Flags.Log': { + category: 'crowdstrike', + description: 'CrowdStrike log flag. ', + name: 'crowdstrike.event.Flags.Log', + type: 'boolean', + }, + 'crowdstrike.event.Flags.Monitor': { + category: 'crowdstrike', + description: 'CrowdStrike monitor flag. ', + name: 'crowdstrike.event.Flags.Monitor', + type: 'boolean', + }, + 'crowdstrike.event.Protocol': { + category: 'crowdstrike', + description: 'CrowdStrike provided protocol. ', + name: 'crowdstrike.event.Protocol', + type: 'keyword', + }, + 'crowdstrike.event.NetworkProfile': { + category: 'crowdstrike', + description: 'CrowdStrike network profile. ', + name: 'crowdstrike.event.NetworkProfile', + type: 'keyword', + }, + 'crowdstrike.event.PolicyName': { + category: 'crowdstrike', + description: 'CrowdStrike policy name. ', + name: 'crowdstrike.event.PolicyName', + type: 'keyword', + }, + 'crowdstrike.event.PolicyID': { + category: 'crowdstrike', + description: 'CrowdStrike policy id. ', + name: 'crowdstrike.event.PolicyID', + type: 'keyword', + }, + 'crowdstrike.event.Status': { + category: 'crowdstrike', + description: 'CrowdStrike status. ', + name: 'crowdstrike.event.Status', + type: 'keyword', + }, + 'crowdstrike.event.TreeID': { + category: 'crowdstrike', + description: 'CrowdStrike tree id. ', + name: 'crowdstrike.event.TreeID', + type: 'keyword', + }, + 'crowdstrike.event.Commands': { + category: 'crowdstrike', + description: 'Commands run in a remote session. ', + name: 'crowdstrike.event.Commands', + type: 'keyword', + }, + 'envoyproxy.log_type': { + category: 'envoyproxy', + description: 'Envoy log type, normally ACCESS ', + name: 'envoyproxy.log_type', + type: 'keyword', + }, + 'envoyproxy.response_flags': { + category: 'envoyproxy', + description: 'Response flags ', + name: 'envoyproxy.response_flags', + type: 'keyword', + }, + 'envoyproxy.upstream_service_time': { + category: 'envoyproxy', + description: 'Upstream service time in nanoseconds ', + name: 'envoyproxy.upstream_service_time', + type: 'long', + format: 'duration', + }, + 'envoyproxy.request_id': { + category: 'envoyproxy', + description: 'ID of the request ', + name: 'envoyproxy.request_id', + type: 'keyword', + }, + 'envoyproxy.authority': { + category: 'envoyproxy', + description: 'Envoy proxy authority field ', + name: 'envoyproxy.authority', + type: 'keyword', + }, + 'envoyproxy.proxy_type': { + category: 'envoyproxy', + description: 'Envoy proxy type, tcp or http ', + name: 'envoyproxy.proxy_type', + type: 'keyword', + }, + 'fortinet.file.hash.crc32': { + category: 'fortinet', + description: 'CRC32 Hash of file ', + name: 'fortinet.file.hash.crc32', + type: 'keyword', + }, + 'fortinet.firewall.acct_stat': { + category: 'fortinet', + description: 'Accounting state (RADIUS) ', + name: 'fortinet.firewall.acct_stat', + type: 'keyword', + }, + 'fortinet.firewall.acktime': { + category: 'fortinet', + description: 'Alarm Acknowledge Time ', + name: 'fortinet.firewall.acktime', + type: 'keyword', + }, + 'fortinet.firewall.act': { + category: 'fortinet', + description: 'Action ', + name: 'fortinet.firewall.act', + type: 'keyword', + }, + 'fortinet.firewall.action': { + category: 'fortinet', + description: 'Status of the session ', + name: 'fortinet.firewall.action', + type: 'keyword', + }, + 'fortinet.firewall.activity': { + category: 'fortinet', + description: 'HA activity message ', + name: 'fortinet.firewall.activity', + type: 'keyword', + }, + 'fortinet.firewall.addr': { + category: 'fortinet', + description: 'IP Address ', + name: 'fortinet.firewall.addr', + type: 'ip', + }, + 'fortinet.firewall.addr_type': { + category: 'fortinet', + description: 'Address Type ', + name: 'fortinet.firewall.addr_type', + type: 'keyword', + }, + 'fortinet.firewall.addrgrp': { + category: 'fortinet', + description: 'Address Group ', + name: 'fortinet.firewall.addrgrp', + type: 'keyword', + }, + 'fortinet.firewall.adgroup': { + category: 'fortinet', + description: 'AD Group Name ', + name: 'fortinet.firewall.adgroup', + type: 'keyword', + }, + 'fortinet.firewall.admin': { + category: 'fortinet', + description: 'Admin User ', + name: 'fortinet.firewall.admin', + type: 'keyword', + }, + 'fortinet.firewall.age': { + category: 'fortinet', + description: 'Time in seconds - time passed since last seen ', + name: 'fortinet.firewall.age', + type: 'integer', + }, + 'fortinet.firewall.agent': { + category: 'fortinet', + description: 'User agent - eg. agent="Mozilla/5.0" ', + name: 'fortinet.firewall.agent', + type: 'keyword', + }, + 'fortinet.firewall.alarmid': { + category: 'fortinet', + description: 'Alarm ID ', + name: 'fortinet.firewall.alarmid', + type: 'integer', + }, + 'fortinet.firewall.alert': { + category: 'fortinet', + description: 'Alert ', + name: 'fortinet.firewall.alert', + type: 'keyword', + }, + 'fortinet.firewall.analyticscksum': { + category: 'fortinet', + description: 'The checksum of the file submitted for analytics ', + name: 'fortinet.firewall.analyticscksum', + type: 'keyword', + }, + 'fortinet.firewall.analyticssubmit': { + category: 'fortinet', + description: 'The flag for analytics submission ', + name: 'fortinet.firewall.analyticssubmit', + type: 'keyword', + }, + 'fortinet.firewall.ap': { + category: 'fortinet', + description: 'Access Point ', + name: 'fortinet.firewall.ap', + type: 'keyword', + }, + 'fortinet.firewall.app-type': { + category: 'fortinet', + description: 'Address Type ', + name: 'fortinet.firewall.app-type', + type: 'keyword', + }, + 'fortinet.firewall.appact': { + category: 'fortinet', + description: 'The security action from app control ', + name: 'fortinet.firewall.appact', + type: 'keyword', + }, + 'fortinet.firewall.appid': { + category: 'fortinet', + description: 'Application ID ', + name: 'fortinet.firewall.appid', + type: 'integer', + }, + 'fortinet.firewall.applist': { + category: 'fortinet', + description: 'Application Control profile ', + name: 'fortinet.firewall.applist', + type: 'keyword', + }, + 'fortinet.firewall.apprisk': { + category: 'fortinet', + description: 'Application Risk Level ', + name: 'fortinet.firewall.apprisk', + type: 'keyword', + }, + 'fortinet.firewall.apscan': { + category: 'fortinet', + description: 'The name of the AP, which scanned and detected the rogue AP ', + name: 'fortinet.firewall.apscan', + type: 'keyword', + }, + 'fortinet.firewall.apsn': { + category: 'fortinet', + description: 'Access Point ', + name: 'fortinet.firewall.apsn', + type: 'keyword', + }, + 'fortinet.firewall.apstatus': { + category: 'fortinet', + description: 'Access Point status ', + name: 'fortinet.firewall.apstatus', + type: 'keyword', + }, + 'fortinet.firewall.aptype': { + category: 'fortinet', + description: 'Access Point type ', + name: 'fortinet.firewall.aptype', + type: 'keyword', + }, + 'fortinet.firewall.assigned': { + category: 'fortinet', + description: 'Assigned IP Address ', + name: 'fortinet.firewall.assigned', + type: 'ip', + }, + 'fortinet.firewall.assignip': { + category: 'fortinet', + description: 'Assigned IP Address ', + name: 'fortinet.firewall.assignip', + type: 'ip', + }, + 'fortinet.firewall.attachment': { + category: 'fortinet', + description: 'The flag for email attachement ', + name: 'fortinet.firewall.attachment', + type: 'keyword', + }, + 'fortinet.firewall.attack': { + category: 'fortinet', + description: 'Attack Name ', + name: 'fortinet.firewall.attack', + type: 'keyword', + }, + 'fortinet.firewall.attackcontext': { + category: 'fortinet', + description: 'The trigger patterns and the packetdata with base64 encoding ', + name: 'fortinet.firewall.attackcontext', + type: 'keyword', + }, + 'fortinet.firewall.attackcontextid': { + category: 'fortinet', + description: 'Attack context id / total ', + name: 'fortinet.firewall.attackcontextid', + type: 'keyword', + }, + 'fortinet.firewall.attackid': { + category: 'fortinet', + description: 'Attack ID ', + name: 'fortinet.firewall.attackid', + type: 'integer', + }, + 'fortinet.firewall.auditid': { + category: 'fortinet', + description: 'Audit ID ', + name: 'fortinet.firewall.auditid', + type: 'long', + }, + 'fortinet.firewall.auditscore': { + category: 'fortinet', + description: 'The Audit Score ', + name: 'fortinet.firewall.auditscore', + type: 'keyword', + }, + 'fortinet.firewall.audittime': { + category: 'fortinet', + description: 'The time of the audit ', + name: 'fortinet.firewall.audittime', + type: 'long', + }, + 'fortinet.firewall.authgrp': { + category: 'fortinet', + description: 'Authorization Group ', + name: 'fortinet.firewall.authgrp', + type: 'keyword', + }, + 'fortinet.firewall.authid': { + category: 'fortinet', + description: 'Authentication ID ', + name: 'fortinet.firewall.authid', + type: 'keyword', + }, + 'fortinet.firewall.authproto': { + category: 'fortinet', + description: 'The protocol that initiated the authentication ', + name: 'fortinet.firewall.authproto', + type: 'keyword', + }, + 'fortinet.firewall.authserver': { + category: 'fortinet', + description: 'Authentication server ', + name: 'fortinet.firewall.authserver', + type: 'keyword', + }, + 'fortinet.firewall.bandwidth': { + category: 'fortinet', + description: 'Bandwidth ', + name: 'fortinet.firewall.bandwidth', + type: 'keyword', + }, + 'fortinet.firewall.banned_rule': { + category: 'fortinet', + description: 'NAC quarantine Banned Rule Name ', + name: 'fortinet.firewall.banned_rule', + type: 'keyword', + }, + 'fortinet.firewall.banned_src': { + category: 'fortinet', + description: 'NAC quarantine Banned Source IP ', + name: 'fortinet.firewall.banned_src', + type: 'keyword', + }, + 'fortinet.firewall.banword': { + category: 'fortinet', + description: 'Banned word ', + name: 'fortinet.firewall.banword', + type: 'keyword', + }, + 'fortinet.firewall.botnetdomain': { + category: 'fortinet', + description: 'Botnet Domain Name ', + name: 'fortinet.firewall.botnetdomain', + type: 'keyword', + }, + 'fortinet.firewall.botnetip': { + category: 'fortinet', + description: 'Botnet IP Address ', + name: 'fortinet.firewall.botnetip', + type: 'ip', + }, + 'fortinet.firewall.bssid': { + category: 'fortinet', + description: 'Service Set ID ', + name: 'fortinet.firewall.bssid', + type: 'keyword', + }, + 'fortinet.firewall.call_id': { + category: 'fortinet', + description: 'Caller ID ', + name: 'fortinet.firewall.call_id', + type: 'keyword', + }, + 'fortinet.firewall.carrier_ep': { + category: 'fortinet', + description: 'The FortiOS Carrier end-point identification ', + name: 'fortinet.firewall.carrier_ep', + type: 'keyword', + }, + 'fortinet.firewall.cat': { + category: 'fortinet', + description: 'DNS category ID ', + name: 'fortinet.firewall.cat', + type: 'integer', + }, + 'fortinet.firewall.category': { + category: 'fortinet', + description: 'Authentication category ', + name: 'fortinet.firewall.category', + type: 'keyword', + }, + 'fortinet.firewall.cc': { + category: 'fortinet', + description: 'CC Email Address ', + name: 'fortinet.firewall.cc', + type: 'keyword', + }, + 'fortinet.firewall.cdrcontent': { + category: 'fortinet', + description: 'Cdrcontent ', + name: 'fortinet.firewall.cdrcontent', + type: 'keyword', + }, + 'fortinet.firewall.centralnatid': { + category: 'fortinet', + description: 'Central NAT ID ', + name: 'fortinet.firewall.centralnatid', + type: 'integer', + }, + 'fortinet.firewall.cert': { + category: 'fortinet', + description: 'Certificate ', + name: 'fortinet.firewall.cert', + type: 'keyword', + }, + 'fortinet.firewall.cert-type': { + category: 'fortinet', + description: 'Certificate type ', + name: 'fortinet.firewall.cert-type', + type: 'keyword', + }, + 'fortinet.firewall.certhash': { + category: 'fortinet', + description: 'Certificate hash ', + name: 'fortinet.firewall.certhash', + type: 'keyword', + }, + 'fortinet.firewall.cfgattr': { + category: 'fortinet', + description: 'Configuration attribute ', + name: 'fortinet.firewall.cfgattr', + type: 'keyword', + }, + 'fortinet.firewall.cfgobj': { + category: 'fortinet', + description: 'Configuration object ', + name: 'fortinet.firewall.cfgobj', + type: 'keyword', + }, + 'fortinet.firewall.cfgpath': { + category: 'fortinet', + description: 'Configuration path ', + name: 'fortinet.firewall.cfgpath', + type: 'keyword', + }, + 'fortinet.firewall.cfgtid': { + category: 'fortinet', + description: 'Configuration transaction ID ', + name: 'fortinet.firewall.cfgtid', + type: 'keyword', + }, + 'fortinet.firewall.cfgtxpower': { + category: 'fortinet', + description: 'Configuration TX power ', + name: 'fortinet.firewall.cfgtxpower', + type: 'integer', + }, + 'fortinet.firewall.channel': { + category: 'fortinet', + description: 'Wireless Channel ', + name: 'fortinet.firewall.channel', + type: 'integer', + }, + 'fortinet.firewall.channeltype': { + category: 'fortinet', + description: 'SSH channel type ', + name: 'fortinet.firewall.channeltype', + type: 'keyword', + }, + 'fortinet.firewall.chassisid': { + category: 'fortinet', + description: 'Chassis ID ', + name: 'fortinet.firewall.chassisid', + type: 'integer', + }, + 'fortinet.firewall.checksum': { + category: 'fortinet', + description: 'The checksum of the scanned file ', + name: 'fortinet.firewall.checksum', + type: 'keyword', + }, + 'fortinet.firewall.chgheaders': { + category: 'fortinet', + description: 'HTTP Headers ', + name: 'fortinet.firewall.chgheaders', + type: 'keyword', + }, + 'fortinet.firewall.cldobjid': { + category: 'fortinet', + description: 'Connector object ID ', + name: 'fortinet.firewall.cldobjid', + type: 'keyword', + }, + 'fortinet.firewall.client_addr': { + category: 'fortinet', + description: 'Wifi client address ', + name: 'fortinet.firewall.client_addr', + type: 'keyword', + }, + 'fortinet.firewall.cloudaction': { + category: 'fortinet', + description: 'Cloud Action ', + name: 'fortinet.firewall.cloudaction', + type: 'keyword', + }, + 'fortinet.firewall.clouduser': { + category: 'fortinet', + description: 'Cloud User ', + name: 'fortinet.firewall.clouduser', + type: 'keyword', + }, + 'fortinet.firewall.column': { + category: 'fortinet', + description: 'VOIP Column ', + name: 'fortinet.firewall.column', + type: 'integer', + }, + 'fortinet.firewall.command': { + category: 'fortinet', + description: 'CLI Command ', + name: 'fortinet.firewall.command', + type: 'keyword', + }, + 'fortinet.firewall.community': { + category: 'fortinet', + description: 'SNMP Community ', + name: 'fortinet.firewall.community', + type: 'keyword', + }, + 'fortinet.firewall.configcountry': { + category: 'fortinet', + description: 'Configuration country ', + name: 'fortinet.firewall.configcountry', + type: 'keyword', + }, + 'fortinet.firewall.connection_type': { + category: 'fortinet', + description: 'FortiClient Connection Type ', + name: 'fortinet.firewall.connection_type', + type: 'keyword', + }, + 'fortinet.firewall.conserve': { + category: 'fortinet', + description: 'Flag for conserve mode ', + name: 'fortinet.firewall.conserve', + type: 'keyword', + }, + 'fortinet.firewall.constraint': { + category: 'fortinet', + description: 'WAF http protocol restrictions ', + name: 'fortinet.firewall.constraint', + type: 'keyword', + }, + 'fortinet.firewall.contentdisarmed': { + category: 'fortinet', + description: 'Email scanned content ', + name: 'fortinet.firewall.contentdisarmed', + type: 'keyword', + }, + 'fortinet.firewall.contenttype': { + category: 'fortinet', + description: 'Content Type from HTTP header ', + name: 'fortinet.firewall.contenttype', + type: 'keyword', + }, + 'fortinet.firewall.cookies': { + category: 'fortinet', + description: 'VPN Cookie ', + name: 'fortinet.firewall.cookies', + type: 'keyword', + }, + 'fortinet.firewall.count': { + category: 'fortinet', + description: 'Counts of action type ', + name: 'fortinet.firewall.count', + type: 'integer', + }, + 'fortinet.firewall.countapp': { + category: 'fortinet', + description: 'Number of App Ctrl logs associated with the session ', + name: 'fortinet.firewall.countapp', + type: 'integer', + }, + 'fortinet.firewall.countav': { + category: 'fortinet', + description: 'Number of AV logs associated with the session ', + name: 'fortinet.firewall.countav', + type: 'integer', + }, + 'fortinet.firewall.countcifs': { + category: 'fortinet', + description: 'Number of CIFS logs associated with the session ', + name: 'fortinet.firewall.countcifs', + type: 'integer', + }, + 'fortinet.firewall.countdlp': { + category: 'fortinet', + description: 'Number of DLP logs associated with the session ', + name: 'fortinet.firewall.countdlp', + type: 'integer', + }, + 'fortinet.firewall.countdns': { + category: 'fortinet', + description: 'Number of DNS logs associated with the session ', + name: 'fortinet.firewall.countdns', + type: 'integer', + }, + 'fortinet.firewall.countemail': { + category: 'fortinet', + description: 'Number of email logs associated with the session ', + name: 'fortinet.firewall.countemail', + type: 'integer', + }, + 'fortinet.firewall.countff': { + category: 'fortinet', + description: 'Number of ff logs associated with the session ', + name: 'fortinet.firewall.countff', + type: 'integer', + }, + 'fortinet.firewall.countips': { + category: 'fortinet', + description: 'Number of IPS logs associated with the session ', + name: 'fortinet.firewall.countips', + type: 'integer', + }, + 'fortinet.firewall.countssh': { + category: 'fortinet', + description: 'Number of SSH logs associated with the session ', + name: 'fortinet.firewall.countssh', + type: 'integer', + }, + 'fortinet.firewall.countssl': { + category: 'fortinet', + description: 'Number of SSL logs associated with the session ', + name: 'fortinet.firewall.countssl', + type: 'integer', + }, + 'fortinet.firewall.countwaf': { + category: 'fortinet', + description: 'Number of WAF logs associated with the session ', + name: 'fortinet.firewall.countwaf', + type: 'integer', + }, + 'fortinet.firewall.countweb': { + category: 'fortinet', + description: 'Number of Web filter logs associated with the session ', + name: 'fortinet.firewall.countweb', + type: 'integer', + }, + 'fortinet.firewall.cpu': { + category: 'fortinet', + description: 'CPU Usage ', + name: 'fortinet.firewall.cpu', + type: 'integer', + }, + 'fortinet.firewall.craction': { + category: 'fortinet', + description: 'Client Reputation Action ', + name: 'fortinet.firewall.craction', + type: 'integer', + }, + 'fortinet.firewall.criticalcount': { + category: 'fortinet', + description: 'Number of critical ratings ', + name: 'fortinet.firewall.criticalcount', + type: 'integer', + }, + 'fortinet.firewall.crl': { + category: 'fortinet', + description: 'Client Reputation Level ', + name: 'fortinet.firewall.crl', + type: 'keyword', + }, + 'fortinet.firewall.crlevel': { + category: 'fortinet', + description: 'Client Reputation Level ', + name: 'fortinet.firewall.crlevel', + type: 'keyword', + }, + 'fortinet.firewall.crscore': { + category: 'fortinet', + description: 'Some description ', + name: 'fortinet.firewall.crscore', + type: 'integer', + }, + 'fortinet.firewall.cveid': { + category: 'fortinet', + description: 'CVE ID ', + name: 'fortinet.firewall.cveid', + type: 'keyword', + }, + 'fortinet.firewall.daemon': { + category: 'fortinet', + description: 'Daemon name ', + name: 'fortinet.firewall.daemon', + type: 'keyword', + }, + 'fortinet.firewall.datarange': { + category: 'fortinet', + description: 'Data range for reports ', + name: 'fortinet.firewall.datarange', + type: 'keyword', + }, + 'fortinet.firewall.date': { + category: 'fortinet', + description: 'Date ', + name: 'fortinet.firewall.date', + type: 'keyword', + }, + 'fortinet.firewall.ddnsserver': { + category: 'fortinet', + description: 'DDNS server ', + name: 'fortinet.firewall.ddnsserver', + type: 'ip', + }, + 'fortinet.firewall.desc': { + category: 'fortinet', + description: 'Description ', + name: 'fortinet.firewall.desc', + type: 'keyword', + }, + 'fortinet.firewall.detectionmethod': { + category: 'fortinet', + description: 'Detection method ', + name: 'fortinet.firewall.detectionmethod', + type: 'keyword', + }, + 'fortinet.firewall.devcategory': { + category: 'fortinet', + description: 'Device category ', + name: 'fortinet.firewall.devcategory', + type: 'keyword', + }, + 'fortinet.firewall.devintfname': { + category: 'fortinet', + description: 'HA device Interface Name ', + name: 'fortinet.firewall.devintfname', + type: 'keyword', + }, + 'fortinet.firewall.devtype': { + category: 'fortinet', + description: 'Device type ', + name: 'fortinet.firewall.devtype', + type: 'keyword', + }, + 'fortinet.firewall.dhcp_msg': { + category: 'fortinet', + description: 'DHCP Message ', + name: 'fortinet.firewall.dhcp_msg', + type: 'keyword', + }, + 'fortinet.firewall.dintf': { + category: 'fortinet', + description: 'Destination interface ', + name: 'fortinet.firewall.dintf', + type: 'keyword', + }, + 'fortinet.firewall.disk': { + category: 'fortinet', + description: 'Assosciated disk ', + name: 'fortinet.firewall.disk', + type: 'keyword', + }, + 'fortinet.firewall.disklograte': { + category: 'fortinet', + description: 'Disk logging rate ', + name: 'fortinet.firewall.disklograte', + type: 'long', + }, + 'fortinet.firewall.dlpextra': { + category: 'fortinet', + description: 'DLP extra information ', + name: 'fortinet.firewall.dlpextra', + type: 'keyword', + }, + 'fortinet.firewall.docsource': { + category: 'fortinet', + description: 'DLP fingerprint document source ', + name: 'fortinet.firewall.docsource', + type: 'keyword', + }, + 'fortinet.firewall.domainctrlauthstate': { + category: 'fortinet', + description: 'CIFS domain auth state ', + name: 'fortinet.firewall.domainctrlauthstate', + type: 'integer', + }, + 'fortinet.firewall.domainctrlauthtype': { + category: 'fortinet', + description: 'CIFS domain auth type ', + name: 'fortinet.firewall.domainctrlauthtype', + type: 'integer', + }, + 'fortinet.firewall.domainctrldomain': { + category: 'fortinet', + description: 'CIFS domain auth domain ', + name: 'fortinet.firewall.domainctrldomain', + type: 'keyword', + }, + 'fortinet.firewall.domainctrlip': { + category: 'fortinet', + description: 'CIFS Domain IP ', + name: 'fortinet.firewall.domainctrlip', + type: 'ip', + }, + 'fortinet.firewall.domainctrlname': { + category: 'fortinet', + description: 'CIFS Domain name ', + name: 'fortinet.firewall.domainctrlname', + type: 'keyword', + }, + 'fortinet.firewall.domainctrlprotocoltype': { + category: 'fortinet', + description: 'CIFS Domain connection protocol ', + name: 'fortinet.firewall.domainctrlprotocoltype', + type: 'integer', + }, + 'fortinet.firewall.domainctrlusername': { + category: 'fortinet', + description: 'CIFS Domain username ', + name: 'fortinet.firewall.domainctrlusername', + type: 'keyword', + }, + 'fortinet.firewall.domainfilteridx': { + category: 'fortinet', + description: 'Domain filter ID ', + name: 'fortinet.firewall.domainfilteridx', + type: 'integer', + }, + 'fortinet.firewall.domainfilterlist': { + category: 'fortinet', + description: 'Domain filter name ', + name: 'fortinet.firewall.domainfilterlist', + type: 'keyword', + }, + 'fortinet.firewall.ds': { + category: 'fortinet', + description: 'Direction with distribution system ', + name: 'fortinet.firewall.ds', + type: 'keyword', + }, + 'fortinet.firewall.dst_int': { + category: 'fortinet', + description: 'Destination interface ', + name: 'fortinet.firewall.dst_int', + type: 'keyword', + }, + 'fortinet.firewall.dstintfrole': { + category: 'fortinet', + description: 'Destination interface role ', + name: 'fortinet.firewall.dstintfrole', + type: 'keyword', + }, + 'fortinet.firewall.dstcountry': { + category: 'fortinet', + description: 'Destination country ', + name: 'fortinet.firewall.dstcountry', + type: 'keyword', + }, + 'fortinet.firewall.dstdevcategory': { + category: 'fortinet', + description: 'Destination device category ', + name: 'fortinet.firewall.dstdevcategory', + type: 'keyword', + }, + 'fortinet.firewall.dstdevtype': { + category: 'fortinet', + description: 'Destination device type ', + name: 'fortinet.firewall.dstdevtype', + type: 'keyword', + }, + 'fortinet.firewall.dstfamily': { + category: 'fortinet', + description: 'Destination OS family ', + name: 'fortinet.firewall.dstfamily', + type: 'keyword', + }, + 'fortinet.firewall.dsthwvendor': { + category: 'fortinet', + description: 'Destination HW vendor ', + name: 'fortinet.firewall.dsthwvendor', + type: 'keyword', + }, + 'fortinet.firewall.dsthwversion': { + category: 'fortinet', + description: 'Destination HW version ', + name: 'fortinet.firewall.dsthwversion', + type: 'keyword', + }, + 'fortinet.firewall.dstinetsvc': { + category: 'fortinet', + description: 'Destination interface service ', + name: 'fortinet.firewall.dstinetsvc', + type: 'keyword', + }, + 'fortinet.firewall.dstosname': { + category: 'fortinet', + description: 'Destination OS name ', + name: 'fortinet.firewall.dstosname', + type: 'keyword', + }, + 'fortinet.firewall.dstosversion': { + category: 'fortinet', + description: 'Destination OS version ', + name: 'fortinet.firewall.dstosversion', + type: 'keyword', + }, + 'fortinet.firewall.dstserver': { + category: 'fortinet', + description: 'Destination server ', + name: 'fortinet.firewall.dstserver', + type: 'integer', + }, + 'fortinet.firewall.dstssid': { + category: 'fortinet', + description: 'Destination SSID ', + name: 'fortinet.firewall.dstssid', + type: 'keyword', + }, + 'fortinet.firewall.dstswversion': { + category: 'fortinet', + description: 'Destination software version ', + name: 'fortinet.firewall.dstswversion', + type: 'keyword', + }, + 'fortinet.firewall.dstunauthusersource': { + category: 'fortinet', + description: 'Destination unauthenticated source ', + name: 'fortinet.firewall.dstunauthusersource', + type: 'keyword', + }, + 'fortinet.firewall.dstuuid': { + category: 'fortinet', + description: 'UUID of the Destination IP address ', + name: 'fortinet.firewall.dstuuid', + type: 'keyword', + }, + 'fortinet.firewall.duid': { + category: 'fortinet', + description: 'DHCP UID ', + name: 'fortinet.firewall.duid', + type: 'keyword', + }, + 'fortinet.firewall.eapolcnt': { + category: 'fortinet', + description: 'EAPOL packet count ', + name: 'fortinet.firewall.eapolcnt', + type: 'integer', + }, + 'fortinet.firewall.eapoltype': { + category: 'fortinet', + description: 'EAPOL packet type ', + name: 'fortinet.firewall.eapoltype', + type: 'keyword', + }, + 'fortinet.firewall.encrypt': { + category: 'fortinet', + description: 'Whether the packet is encrypted or not ', + name: 'fortinet.firewall.encrypt', + type: 'integer', + }, + 'fortinet.firewall.encryption': { + category: 'fortinet', + description: 'Encryption method ', + name: 'fortinet.firewall.encryption', + type: 'keyword', + }, + 'fortinet.firewall.epoch': { + category: 'fortinet', + description: 'Epoch used for locating file ', + name: 'fortinet.firewall.epoch', + type: 'integer', + }, + 'fortinet.firewall.espauth': { + category: 'fortinet', + description: 'ESP Authentication ', + name: 'fortinet.firewall.espauth', + type: 'keyword', + }, + 'fortinet.firewall.esptransform': { + category: 'fortinet', + description: 'ESP Transform ', + name: 'fortinet.firewall.esptransform', + type: 'keyword', + }, + 'fortinet.firewall.exch': { + category: 'fortinet', + description: 'Mail Exchanges from DNS response answer section ', + name: 'fortinet.firewall.exch', + type: 'keyword', + }, + 'fortinet.firewall.exchange': { + category: 'fortinet', + description: 'Mail Exchanges from DNS response answer section ', + name: 'fortinet.firewall.exchange', + type: 'keyword', + }, + 'fortinet.firewall.expectedsignature': { + category: 'fortinet', + description: 'Expected SSL signature ', + name: 'fortinet.firewall.expectedsignature', + type: 'keyword', + }, + 'fortinet.firewall.expiry': { + category: 'fortinet', + description: 'FortiGuard override expiry timestamp ', + name: 'fortinet.firewall.expiry', + type: 'keyword', + }, + 'fortinet.firewall.fams_pause': { + category: 'fortinet', + description: 'Fortinet Analysis and Management Service Pause ', + name: 'fortinet.firewall.fams_pause', + type: 'integer', + }, + 'fortinet.firewall.fazlograte': { + category: 'fortinet', + description: 'FortiAnalyzer Logging Rate ', + name: 'fortinet.firewall.fazlograte', + type: 'long', + }, + 'fortinet.firewall.fctemssn': { + category: 'fortinet', + description: 'FortiClient Endpoint SSN ', + name: 'fortinet.firewall.fctemssn', + type: 'keyword', + }, + 'fortinet.firewall.fctuid': { + category: 'fortinet', + description: 'FortiClient UID ', + name: 'fortinet.firewall.fctuid', + type: 'keyword', + }, + 'fortinet.firewall.field': { + category: 'fortinet', + description: 'NTP status field ', + name: 'fortinet.firewall.field', + type: 'keyword', + }, + 'fortinet.firewall.filefilter': { + category: 'fortinet', + description: 'The filter used to identify the affected file ', + name: 'fortinet.firewall.filefilter', + type: 'keyword', + }, + 'fortinet.firewall.filehashsrc': { + category: 'fortinet', + description: 'Filehash source ', + name: 'fortinet.firewall.filehashsrc', + type: 'keyword', + }, + 'fortinet.firewall.filtercat': { + category: 'fortinet', + description: 'DLP filter category ', + name: 'fortinet.firewall.filtercat', + type: 'keyword', + }, + 'fortinet.firewall.filteridx': { + category: 'fortinet', + description: 'DLP filter ID ', + name: 'fortinet.firewall.filteridx', + type: 'integer', + }, + 'fortinet.firewall.filtername': { + category: 'fortinet', + description: 'DLP rule name ', + name: 'fortinet.firewall.filtername', + type: 'keyword', + }, + 'fortinet.firewall.filtertype': { + category: 'fortinet', + description: 'DLP filter type ', + name: 'fortinet.firewall.filtertype', + type: 'keyword', + }, + 'fortinet.firewall.fortiguardresp': { + category: 'fortinet', + description: 'Antispam ESP value ', + name: 'fortinet.firewall.fortiguardresp', + type: 'keyword', + }, + 'fortinet.firewall.forwardedfor': { + category: 'fortinet', + description: 'Email address forwarded ', + name: 'fortinet.firewall.forwardedfor', + type: 'keyword', + }, + 'fortinet.firewall.fqdn': { + category: 'fortinet', + description: 'FQDN ', + name: 'fortinet.firewall.fqdn', + type: 'keyword', + }, + 'fortinet.firewall.frametype': { + category: 'fortinet', + description: 'Wireless frametype ', + name: 'fortinet.firewall.frametype', + type: 'keyword', + }, + 'fortinet.firewall.freediskstorage': { + category: 'fortinet', + description: 'Free disk integer ', + name: 'fortinet.firewall.freediskstorage', + type: 'integer', + }, + 'fortinet.firewall.from': { + category: 'fortinet', + description: 'From email address ', + name: 'fortinet.firewall.from', + type: 'keyword', + }, + 'fortinet.firewall.from_vcluster': { + category: 'fortinet', + description: 'Source virtual cluster number ', + name: 'fortinet.firewall.from_vcluster', + type: 'integer', + }, + 'fortinet.firewall.fsaverdict': { + category: 'fortinet', + description: 'FSA verdict ', + name: 'fortinet.firewall.fsaverdict', + type: 'keyword', + }, + 'fortinet.firewall.fwserver_name': { + category: 'fortinet', + description: 'Web proxy server name ', + name: 'fortinet.firewall.fwserver_name', + type: 'keyword', + }, + 'fortinet.firewall.gateway': { + category: 'fortinet', + description: 'Gateway ip address for PPPoE status report ', + name: 'fortinet.firewall.gateway', + type: 'ip', + }, + 'fortinet.firewall.green': { + category: 'fortinet', + description: 'Memory status ', + name: 'fortinet.firewall.green', + type: 'keyword', + }, + 'fortinet.firewall.groupid': { + category: 'fortinet', + description: 'User Group ID ', + name: 'fortinet.firewall.groupid', + type: 'integer', + }, + 'fortinet.firewall.ha-prio': { + category: 'fortinet', + description: 'HA Priority ', + name: 'fortinet.firewall.ha-prio', + type: 'integer', + }, + 'fortinet.firewall.ha_group': { + category: 'fortinet', + description: 'HA Group ', + name: 'fortinet.firewall.ha_group', + type: 'keyword', + }, + 'fortinet.firewall.ha_role': { + category: 'fortinet', + description: 'HA Role ', + name: 'fortinet.firewall.ha_role', + type: 'keyword', + }, + 'fortinet.firewall.handshake': { + category: 'fortinet', + description: 'SSL Handshake ', + name: 'fortinet.firewall.handshake', + type: 'keyword', + }, + 'fortinet.firewall.hash': { + category: 'fortinet', + description: 'Hash value of downloaded file ', + name: 'fortinet.firewall.hash', + type: 'keyword', + }, + 'fortinet.firewall.hbdn_reason': { + category: 'fortinet', + description: 'Heartbeat down reason ', + name: 'fortinet.firewall.hbdn_reason', + type: 'keyword', + }, + 'fortinet.firewall.highcount': { + category: 'fortinet', + description: 'Highcount fabric summary ', + name: 'fortinet.firewall.highcount', + type: 'integer', + }, + 'fortinet.firewall.host': { + category: 'fortinet', + description: 'Hostname ', + name: 'fortinet.firewall.host', + type: 'keyword', + }, + 'fortinet.firewall.iaid': { + category: 'fortinet', + description: 'DHCPv6 id ', + name: 'fortinet.firewall.iaid', + type: 'keyword', + }, + 'fortinet.firewall.icmpcode': { + category: 'fortinet', + description: 'Destination Port of the ICMP message ', + name: 'fortinet.firewall.icmpcode', + type: 'keyword', + }, + 'fortinet.firewall.icmpid': { + category: 'fortinet', + description: 'Source port of the ICMP message ', + name: 'fortinet.firewall.icmpid', + type: 'keyword', + }, + 'fortinet.firewall.icmptype': { + category: 'fortinet', + description: 'The type of ICMP message ', + name: 'fortinet.firewall.icmptype', + type: 'keyword', + }, + 'fortinet.firewall.identifier': { + category: 'fortinet', + description: 'Network traffic identifier ', + name: 'fortinet.firewall.identifier', + type: 'integer', + }, + 'fortinet.firewall.in_spi': { + category: 'fortinet', + description: 'IPSEC inbound SPI ', + name: 'fortinet.firewall.in_spi', + type: 'keyword', + }, + 'fortinet.firewall.incidentserialno': { + category: 'fortinet', + description: 'Incident serial number ', + name: 'fortinet.firewall.incidentserialno', + type: 'integer', + }, + 'fortinet.firewall.infected': { + category: 'fortinet', + description: 'Infected MMS ', + name: 'fortinet.firewall.infected', + type: 'integer', + }, + 'fortinet.firewall.infectedfilelevel': { + category: 'fortinet', + description: 'DLP infected file level ', + name: 'fortinet.firewall.infectedfilelevel', + type: 'integer', + }, + 'fortinet.firewall.informationsource': { + category: 'fortinet', + description: 'Information source ', + name: 'fortinet.firewall.informationsource', + type: 'keyword', + }, + 'fortinet.firewall.init': { + category: 'fortinet', + description: 'IPSEC init stage ', + name: 'fortinet.firewall.init', + type: 'keyword', + }, + 'fortinet.firewall.initiator': { + category: 'fortinet', + description: 'Original login user name for Fortiguard override ', + name: 'fortinet.firewall.initiator', + type: 'keyword', + }, + 'fortinet.firewall.interface': { + category: 'fortinet', + description: 'Related interface ', + name: 'fortinet.firewall.interface', + type: 'keyword', + }, + 'fortinet.firewall.intf': { + category: 'fortinet', + description: 'Related interface ', + name: 'fortinet.firewall.intf', + type: 'keyword', + }, + 'fortinet.firewall.invalidmac': { + category: 'fortinet', + description: 'The MAC address with invalid OUI ', + name: 'fortinet.firewall.invalidmac', + type: 'keyword', + }, + 'fortinet.firewall.ip': { + category: 'fortinet', + description: 'Related IP ', + name: 'fortinet.firewall.ip', + type: 'ip', + }, + 'fortinet.firewall.iptype': { + category: 'fortinet', + description: 'Related IP type ', + name: 'fortinet.firewall.iptype', + type: 'keyword', + }, + 'fortinet.firewall.keyword': { + category: 'fortinet', + description: 'Keyword used for search ', + name: 'fortinet.firewall.keyword', + type: 'keyword', + }, + 'fortinet.firewall.kind': { + category: 'fortinet', + description: 'VOIP kind ', + name: 'fortinet.firewall.kind', + type: 'keyword', + }, + 'fortinet.firewall.lanin': { + category: 'fortinet', + description: 'LAN incoming traffic in bytes ', + name: 'fortinet.firewall.lanin', + type: 'long', + }, + 'fortinet.firewall.lanout': { + category: 'fortinet', + description: 'LAN outbound traffic in bytes ', + name: 'fortinet.firewall.lanout', + type: 'long', + }, + 'fortinet.firewall.lease': { + category: 'fortinet', + description: 'DHCP lease ', + name: 'fortinet.firewall.lease', + type: 'integer', + }, + 'fortinet.firewall.license_limit': { + category: 'fortinet', + description: 'Maximum Number of FortiClients for the License ', + name: 'fortinet.firewall.license_limit', + type: 'keyword', + }, + 'fortinet.firewall.limit': { + category: 'fortinet', + description: 'Virtual Domain Resource Limit ', + name: 'fortinet.firewall.limit', + type: 'integer', + }, + 'fortinet.firewall.line': { + category: 'fortinet', + description: 'VOIP line ', + name: 'fortinet.firewall.line', + type: 'keyword', + }, + 'fortinet.firewall.live': { + category: 'fortinet', + description: 'Time in seconds ', + name: 'fortinet.firewall.live', + type: 'integer', + }, + 'fortinet.firewall.local': { + category: 'fortinet', + description: 'Local IP for a PPPD Connection ', + name: 'fortinet.firewall.local', + type: 'ip', + }, + 'fortinet.firewall.log': { + category: 'fortinet', + description: 'Log message ', + name: 'fortinet.firewall.log', + type: 'keyword', + }, + 'fortinet.firewall.login': { + category: 'fortinet', + description: 'SSH login ', + name: 'fortinet.firewall.login', + type: 'keyword', + }, + 'fortinet.firewall.lowcount': { + category: 'fortinet', + description: 'Fabric lowcount ', + name: 'fortinet.firewall.lowcount', + type: 'integer', + }, + 'fortinet.firewall.mac': { + category: 'fortinet', + description: 'DHCP mac address ', + name: 'fortinet.firewall.mac', + type: 'keyword', + }, + 'fortinet.firewall.malform_data': { + category: 'fortinet', + description: 'VOIP malformed data ', + name: 'fortinet.firewall.malform_data', + type: 'integer', + }, + 'fortinet.firewall.malform_desc': { + category: 'fortinet', + description: 'VOIP malformed data description ', + name: 'fortinet.firewall.malform_desc', + type: 'keyword', + }, + 'fortinet.firewall.manuf': { + category: 'fortinet', + description: 'Manufacturer name ', + name: 'fortinet.firewall.manuf', + type: 'keyword', + }, + 'fortinet.firewall.masterdstmac': { + category: 'fortinet', + description: 'Master mac address for a host with multiple network interfaces ', + name: 'fortinet.firewall.masterdstmac', + type: 'keyword', + }, + 'fortinet.firewall.mastersrcmac': { + category: 'fortinet', + description: 'The master MAC address for a host that has multiple network interfaces ', + name: 'fortinet.firewall.mastersrcmac', + type: 'keyword', + }, + 'fortinet.firewall.mediumcount': { + category: 'fortinet', + description: 'Fabric medium count ', + name: 'fortinet.firewall.mediumcount', + type: 'integer', + }, + 'fortinet.firewall.mem': { + category: 'fortinet', + description: 'Memory usage system statistics ', + name: 'fortinet.firewall.mem', + type: 'keyword', + }, + 'fortinet.firewall.meshmode': { + category: 'fortinet', + description: 'Wireless mesh mode ', + name: 'fortinet.firewall.meshmode', + type: 'keyword', + }, + 'fortinet.firewall.message_type': { + category: 'fortinet', + description: 'VOIP message type ', + name: 'fortinet.firewall.message_type', + type: 'keyword', + }, + 'fortinet.firewall.method': { + category: 'fortinet', + description: 'HTTP method ', + name: 'fortinet.firewall.method', + type: 'keyword', + }, + 'fortinet.firewall.mgmtcnt': { + category: 'fortinet', + description: 'The number of unauthorized client flooding managemet frames ', + name: 'fortinet.firewall.mgmtcnt', + type: 'integer', + }, + 'fortinet.firewall.mode': { + category: 'fortinet', + description: 'IPSEC mode ', + name: 'fortinet.firewall.mode', + type: 'keyword', + }, + 'fortinet.firewall.module': { + category: 'fortinet', + description: 'PCI-DSS module ', + name: 'fortinet.firewall.module', + type: 'keyword', + }, + 'fortinet.firewall.monitor-name': { + category: 'fortinet', + description: 'Health Monitor Name ', + name: 'fortinet.firewall.monitor-name', + type: 'keyword', + }, + 'fortinet.firewall.monitor-type': { + category: 'fortinet', + description: 'Health Monitor Type ', + name: 'fortinet.firewall.monitor-type', + type: 'keyword', + }, + 'fortinet.firewall.mpsk': { + category: 'fortinet', + description: 'Wireless MPSK ', + name: 'fortinet.firewall.mpsk', + type: 'keyword', + }, + 'fortinet.firewall.msgproto': { + category: 'fortinet', + description: 'Message Protocol Number ', + name: 'fortinet.firewall.msgproto', + type: 'keyword', + }, + 'fortinet.firewall.mtu': { + category: 'fortinet', + description: 'Max Transmission Unit Value ', + name: 'fortinet.firewall.mtu', + type: 'integer', + }, + 'fortinet.firewall.name': { + category: 'fortinet', + description: 'Name ', + name: 'fortinet.firewall.name', + type: 'keyword', + }, + 'fortinet.firewall.nat': { + category: 'fortinet', + description: 'NAT IP Address ', + name: 'fortinet.firewall.nat', + type: 'keyword', + }, + 'fortinet.firewall.netid': { + category: 'fortinet', + description: 'Connector NetID ', + name: 'fortinet.firewall.netid', + type: 'keyword', + }, + 'fortinet.firewall.new_status': { + category: 'fortinet', + description: 'New status on user change ', + name: 'fortinet.firewall.new_status', + type: 'keyword', + }, + 'fortinet.firewall.new_value': { + category: 'fortinet', + description: 'New Virtual Domain Name ', + name: 'fortinet.firewall.new_value', + type: 'keyword', + }, + 'fortinet.firewall.newchannel': { + category: 'fortinet', + description: 'New Channel Number ', + name: 'fortinet.firewall.newchannel', + type: 'integer', + }, + 'fortinet.firewall.newchassisid': { + category: 'fortinet', + description: 'New Chassis ID ', + name: 'fortinet.firewall.newchassisid', + type: 'integer', + }, + 'fortinet.firewall.newslot': { + category: 'fortinet', + description: 'New Slot Number ', + name: 'fortinet.firewall.newslot', + type: 'integer', + }, + 'fortinet.firewall.nextstat': { + category: 'fortinet', + description: 'Time interval in seconds for the next statistics. ', + name: 'fortinet.firewall.nextstat', + type: 'integer', + }, + 'fortinet.firewall.nf_type': { + category: 'fortinet', + description: 'Notification Type ', + name: 'fortinet.firewall.nf_type', + type: 'keyword', + }, + 'fortinet.firewall.noise': { + category: 'fortinet', + description: 'Wifi Noise ', + name: 'fortinet.firewall.noise', + type: 'integer', + }, + 'fortinet.firewall.old_status': { + category: 'fortinet', + description: 'Original Status ', + name: 'fortinet.firewall.old_status', + type: 'keyword', + }, + 'fortinet.firewall.old_value': { + category: 'fortinet', + description: 'Original Virtual Domain name ', + name: 'fortinet.firewall.old_value', + type: 'keyword', + }, + 'fortinet.firewall.oldchannel': { + category: 'fortinet', + description: 'Original channel ', + name: 'fortinet.firewall.oldchannel', + type: 'integer', + }, + 'fortinet.firewall.oldchassisid': { + category: 'fortinet', + description: 'Original Chassis Number ', + name: 'fortinet.firewall.oldchassisid', + type: 'integer', + }, + 'fortinet.firewall.oldslot': { + category: 'fortinet', + description: 'Original Slot Number ', + name: 'fortinet.firewall.oldslot', + type: 'integer', + }, + 'fortinet.firewall.oldsn': { + category: 'fortinet', + description: 'Old Serial number ', + name: 'fortinet.firewall.oldsn', + type: 'keyword', + }, + 'fortinet.firewall.oldwprof': { + category: 'fortinet', + description: 'Old Web Filter Profile ', + name: 'fortinet.firewall.oldwprof', + type: 'keyword', + }, + 'fortinet.firewall.onwire': { + category: 'fortinet', + description: 'A flag to indicate if the AP is onwire or not ', + name: 'fortinet.firewall.onwire', + type: 'keyword', + }, + 'fortinet.firewall.opercountry': { + category: 'fortinet', + description: 'Operating Country ', + name: 'fortinet.firewall.opercountry', + type: 'keyword', + }, + 'fortinet.firewall.opertxpower': { + category: 'fortinet', + description: 'Operating TX power ', + name: 'fortinet.firewall.opertxpower', + type: 'integer', + }, + 'fortinet.firewall.osname': { + category: 'fortinet', + description: 'Operating System name ', + name: 'fortinet.firewall.osname', + type: 'keyword', + }, + 'fortinet.firewall.osversion': { + category: 'fortinet', + description: 'Operating System version ', + name: 'fortinet.firewall.osversion', + type: 'keyword', + }, + 'fortinet.firewall.out_spi': { + category: 'fortinet', + description: 'Out SPI ', + name: 'fortinet.firewall.out_spi', + type: 'keyword', + }, + 'fortinet.firewall.outintf': { + category: 'fortinet', + description: 'Out interface ', + name: 'fortinet.firewall.outintf', + type: 'keyword', + }, + 'fortinet.firewall.passedcount': { + category: 'fortinet', + description: 'Fabric passed count ', + name: 'fortinet.firewall.passedcount', + type: 'integer', + }, + 'fortinet.firewall.passwd': { + category: 'fortinet', + description: 'Changed user password information ', + name: 'fortinet.firewall.passwd', + type: 'keyword', + }, + 'fortinet.firewall.path': { + category: 'fortinet', + description: 'Path of looped configuration for security fabric ', + name: 'fortinet.firewall.path', + type: 'keyword', + }, + 'fortinet.firewall.peer': { + category: 'fortinet', + description: 'WAN optimization peer ', + name: 'fortinet.firewall.peer', + type: 'keyword', + }, + 'fortinet.firewall.peer_notif': { + category: 'fortinet', + description: 'VPN peer notification ', + name: 'fortinet.firewall.peer_notif', + type: 'keyword', + }, + 'fortinet.firewall.phase2_name': { + category: 'fortinet', + description: 'VPN phase2 name ', + name: 'fortinet.firewall.phase2_name', + type: 'keyword', + }, + 'fortinet.firewall.phone': { + category: 'fortinet', + description: 'VOIP Phone ', + name: 'fortinet.firewall.phone', + type: 'keyword', + }, + 'fortinet.firewall.pid': { + category: 'fortinet', + description: 'Process ID ', + name: 'fortinet.firewall.pid', + type: 'integer', + }, + 'fortinet.firewall.policytype': { + category: 'fortinet', + description: 'Policy Type ', + name: 'fortinet.firewall.policytype', + type: 'keyword', + }, + 'fortinet.firewall.poolname': { + category: 'fortinet', + description: 'IP Pool name ', + name: 'fortinet.firewall.poolname', + type: 'keyword', + }, + 'fortinet.firewall.port': { + category: 'fortinet', + description: 'Log upload error port ', + name: 'fortinet.firewall.port', + type: 'integer', + }, + 'fortinet.firewall.portbegin': { + category: 'fortinet', + description: 'IP Pool port number to begin ', + name: 'fortinet.firewall.portbegin', + type: 'integer', + }, + 'fortinet.firewall.portend': { + category: 'fortinet', + description: 'IP Pool port number to end ', + name: 'fortinet.firewall.portend', + type: 'integer', + }, + 'fortinet.firewall.probeproto': { + category: 'fortinet', + description: 'Link Monitor Probe Protocol ', + name: 'fortinet.firewall.probeproto', + type: 'keyword', + }, + 'fortinet.firewall.process': { + category: 'fortinet', + description: 'URL Filter process ', + name: 'fortinet.firewall.process', + type: 'keyword', + }, + 'fortinet.firewall.processtime': { + category: 'fortinet', + description: 'Process time for reports ', + name: 'fortinet.firewall.processtime', + type: 'integer', + }, + 'fortinet.firewall.profile': { + category: 'fortinet', + description: 'Profile Name ', + name: 'fortinet.firewall.profile', + type: 'keyword', + }, + 'fortinet.firewall.profile_vd': { + category: 'fortinet', + description: 'Virtual Domain Name ', + name: 'fortinet.firewall.profile_vd', + type: 'keyword', + }, + 'fortinet.firewall.profilegroup': { + category: 'fortinet', + description: 'Profile Group Name ', + name: 'fortinet.firewall.profilegroup', + type: 'keyword', + }, + 'fortinet.firewall.profiletype': { + category: 'fortinet', + description: 'Profile Type ', + name: 'fortinet.firewall.profiletype', + type: 'keyword', + }, + 'fortinet.firewall.qtypeval': { + category: 'fortinet', + description: 'DNS question type value ', + name: 'fortinet.firewall.qtypeval', + type: 'integer', + }, + 'fortinet.firewall.quarskip': { + category: 'fortinet', + description: 'Quarantine skip explanation ', + name: 'fortinet.firewall.quarskip', + type: 'keyword', + }, + 'fortinet.firewall.quotaexceeded': { + category: 'fortinet', + description: 'If quota has been exceeded ', + name: 'fortinet.firewall.quotaexceeded', + type: 'keyword', + }, + 'fortinet.firewall.quotamax': { + category: 'fortinet', + description: 'Maximum quota allowed - in seconds if time-based - in bytes if traffic-based ', + name: 'fortinet.firewall.quotamax', + type: 'long', + }, + 'fortinet.firewall.quotatype': { + category: 'fortinet', + description: 'Quota type ', + name: 'fortinet.firewall.quotatype', + type: 'keyword', + }, + 'fortinet.firewall.quotaused': { + category: 'fortinet', + description: 'Quota used - in seconds if time-based - in bytes if trafficbased) ', + name: 'fortinet.firewall.quotaused', + type: 'long', + }, + 'fortinet.firewall.radioband': { + category: 'fortinet', + description: 'Radio band ', + name: 'fortinet.firewall.radioband', + type: 'keyword', + }, + 'fortinet.firewall.radioid': { + category: 'fortinet', + description: 'Radio ID ', + name: 'fortinet.firewall.radioid', + type: 'integer', + }, + 'fortinet.firewall.radioidclosest': { + category: 'fortinet', + description: 'Radio ID on the AP closest the rogue AP ', + name: 'fortinet.firewall.radioidclosest', + type: 'integer', + }, + 'fortinet.firewall.radioiddetected': { + category: 'fortinet', + description: 'Radio ID on the AP which detected the rogue AP ', + name: 'fortinet.firewall.radioiddetected', + type: 'integer', + }, + 'fortinet.firewall.rate': { + category: 'fortinet', + description: 'Wireless rogue rate value ', + name: 'fortinet.firewall.rate', + type: 'keyword', + }, + 'fortinet.firewall.rawdata': { + category: 'fortinet', + description: 'Raw data value ', + name: 'fortinet.firewall.rawdata', + type: 'keyword', + }, + 'fortinet.firewall.rawdataid': { + category: 'fortinet', + description: 'Raw data ID ', + name: 'fortinet.firewall.rawdataid', + type: 'keyword', + }, + 'fortinet.firewall.rcvddelta': { + category: 'fortinet', + description: 'Received bytes delta ', + name: 'fortinet.firewall.rcvddelta', + type: 'keyword', + }, + 'fortinet.firewall.reason': { + category: 'fortinet', + description: 'Alert reason ', + name: 'fortinet.firewall.reason', + type: 'keyword', + }, + 'fortinet.firewall.received': { + category: 'fortinet', + description: 'Server key exchange received ', + name: 'fortinet.firewall.received', + type: 'integer', + }, + 'fortinet.firewall.receivedsignature': { + category: 'fortinet', + description: 'Server key exchange received signature ', + name: 'fortinet.firewall.receivedsignature', + type: 'keyword', + }, + 'fortinet.firewall.red': { + category: 'fortinet', + description: 'Memory information in red ', + name: 'fortinet.firewall.red', + type: 'keyword', + }, + 'fortinet.firewall.referralurl': { + category: 'fortinet', + description: 'Web filter referralurl ', + name: 'fortinet.firewall.referralurl', + type: 'keyword', + }, + 'fortinet.firewall.remote': { + category: 'fortinet', + description: 'Remote PPP IP address ', + name: 'fortinet.firewall.remote', + type: 'ip', + }, + 'fortinet.firewall.remotewtptime': { + category: 'fortinet', + description: 'Remote Wifi Radius authentication time ', + name: 'fortinet.firewall.remotewtptime', + type: 'keyword', + }, + 'fortinet.firewall.reporttype': { + category: 'fortinet', + description: 'Report type ', + name: 'fortinet.firewall.reporttype', + type: 'keyword', + }, + 'fortinet.firewall.reqtype': { + category: 'fortinet', + description: 'Request type ', + name: 'fortinet.firewall.reqtype', + type: 'keyword', + }, + 'fortinet.firewall.request_name': { + category: 'fortinet', + description: 'VOIP request name ', + name: 'fortinet.firewall.request_name', + type: 'keyword', + }, + 'fortinet.firewall.result': { + category: 'fortinet', + description: 'VPN phase result ', + name: 'fortinet.firewall.result', + type: 'keyword', + }, + 'fortinet.firewall.role': { + category: 'fortinet', + description: 'VPN Phase 2 role ', + name: 'fortinet.firewall.role', + type: 'keyword', + }, + 'fortinet.firewall.rssi': { + category: 'fortinet', + description: 'Received signal strength indicator ', + name: 'fortinet.firewall.rssi', + type: 'integer', + }, + 'fortinet.firewall.rsso_key': { + category: 'fortinet', + description: 'RADIUS SSO attribute value ', + name: 'fortinet.firewall.rsso_key', + type: 'keyword', + }, + 'fortinet.firewall.ruledata': { + category: 'fortinet', + description: 'Rule data ', + name: 'fortinet.firewall.ruledata', + type: 'keyword', + }, + 'fortinet.firewall.ruletype': { + category: 'fortinet', + description: 'Rule type ', + name: 'fortinet.firewall.ruletype', + type: 'keyword', + }, + 'fortinet.firewall.scanned': { + category: 'fortinet', + description: 'Number of Scanned MMSs ', + name: 'fortinet.firewall.scanned', + type: 'integer', + }, + 'fortinet.firewall.scantime': { + category: 'fortinet', + description: 'Scanned time ', + name: 'fortinet.firewall.scantime', + type: 'long', + }, + 'fortinet.firewall.scope': { + category: 'fortinet', + description: 'FortiGuard Override Scope ', + name: 'fortinet.firewall.scope', + type: 'keyword', + }, + 'fortinet.firewall.security': { + category: 'fortinet', + description: 'Wireless rogue security ', + name: 'fortinet.firewall.security', + type: 'keyword', + }, + 'fortinet.firewall.sensitivity': { + category: 'fortinet', + description: 'Sensitivity for document fingerprint ', + name: 'fortinet.firewall.sensitivity', + type: 'keyword', + }, + 'fortinet.firewall.sensor': { + category: 'fortinet', + description: 'NAC Sensor Name ', + name: 'fortinet.firewall.sensor', + type: 'keyword', + }, + 'fortinet.firewall.sentdelta': { + category: 'fortinet', + description: 'Sent bytes delta ', + name: 'fortinet.firewall.sentdelta', + type: 'keyword', + }, + 'fortinet.firewall.seq': { + category: 'fortinet', + description: 'Sequence number ', + name: 'fortinet.firewall.seq', + type: 'keyword', + }, + 'fortinet.firewall.serial': { + category: 'fortinet', + description: 'WAN optimisation serial ', + name: 'fortinet.firewall.serial', + type: 'keyword', + }, + 'fortinet.firewall.serialno': { + category: 'fortinet', + description: 'Serial number ', + name: 'fortinet.firewall.serialno', + type: 'keyword', + }, + 'fortinet.firewall.server': { + category: 'fortinet', + description: 'AD server FQDN or IP ', + name: 'fortinet.firewall.server', + type: 'keyword', + }, + 'fortinet.firewall.session_id': { + category: 'fortinet', + description: 'Session ID ', + name: 'fortinet.firewall.session_id', + type: 'keyword', + }, + 'fortinet.firewall.sessionid': { + category: 'fortinet', + description: 'WAD Session ID ', + name: 'fortinet.firewall.sessionid', + type: 'integer', + }, + 'fortinet.firewall.setuprate': { + category: 'fortinet', + description: 'Session Setup Rate ', + name: 'fortinet.firewall.setuprate', + type: 'long', + }, + 'fortinet.firewall.severity': { + category: 'fortinet', + description: 'Severity ', + name: 'fortinet.firewall.severity', + type: 'keyword', + }, + 'fortinet.firewall.shaperdroprcvdbyte': { + category: 'fortinet', + description: 'Received bytes dropped by shaper ', + name: 'fortinet.firewall.shaperdroprcvdbyte', + type: 'integer', + }, + 'fortinet.firewall.shaperdropsentbyte': { + category: 'fortinet', + description: 'Sent bytes dropped by shaper ', + name: 'fortinet.firewall.shaperdropsentbyte', + type: 'integer', + }, + 'fortinet.firewall.shaperperipdropbyte': { + category: 'fortinet', + description: 'Dropped bytes per IP by shaper ', + name: 'fortinet.firewall.shaperperipdropbyte', + type: 'integer', + }, + 'fortinet.firewall.shaperperipname': { + category: 'fortinet', + description: 'Traffic shaper name (per IP) ', + name: 'fortinet.firewall.shaperperipname', + type: 'keyword', + }, + 'fortinet.firewall.shaperrcvdname': { + category: 'fortinet', + description: 'Traffic shaper name for received traffic ', + name: 'fortinet.firewall.shaperrcvdname', + type: 'keyword', + }, + 'fortinet.firewall.shapersentname': { + category: 'fortinet', + description: 'Traffic shaper name for sent traffic ', + name: 'fortinet.firewall.shapersentname', + type: 'keyword', + }, + 'fortinet.firewall.shapingpolicyid': { + category: 'fortinet', + description: 'Traffic shaper policy ID ', + name: 'fortinet.firewall.shapingpolicyid', + type: 'integer', + }, + 'fortinet.firewall.signal': { + category: 'fortinet', + description: 'Wireless rogue API signal ', + name: 'fortinet.firewall.signal', + type: 'integer', + }, + 'fortinet.firewall.size': { + category: 'fortinet', + description: 'Email size in bytes ', + name: 'fortinet.firewall.size', + type: 'long', + }, + 'fortinet.firewall.slot': { + category: 'fortinet', + description: 'Slot number ', + name: 'fortinet.firewall.slot', + type: 'integer', + }, + 'fortinet.firewall.sn': { + category: 'fortinet', + description: 'Security fabric serial number ', + name: 'fortinet.firewall.sn', + type: 'keyword', + }, + 'fortinet.firewall.snclosest': { + category: 'fortinet', + description: 'SN of the AP closest to the rogue AP ', + name: 'fortinet.firewall.snclosest', + type: 'keyword', + }, + 'fortinet.firewall.sndetected': { + category: 'fortinet', + description: 'SN of the AP which detected the rogue AP ', + name: 'fortinet.firewall.sndetected', + type: 'keyword', + }, + 'fortinet.firewall.snmeshparent': { + category: 'fortinet', + description: 'SN of the mesh parent ', + name: 'fortinet.firewall.snmeshparent', + type: 'keyword', + }, + 'fortinet.firewall.spi': { + category: 'fortinet', + description: 'IPSEC SPI ', + name: 'fortinet.firewall.spi', + type: 'keyword', + }, + 'fortinet.firewall.src_int': { + category: 'fortinet', + description: 'Source interface ', + name: 'fortinet.firewall.src_int', + type: 'keyword', + }, + 'fortinet.firewall.srcintfrole': { + category: 'fortinet', + description: 'Source interface role ', + name: 'fortinet.firewall.srcintfrole', + type: 'keyword', + }, + 'fortinet.firewall.srccountry': { + category: 'fortinet', + description: 'Source country ', + name: 'fortinet.firewall.srccountry', + type: 'keyword', + }, + 'fortinet.firewall.srcfamily': { + category: 'fortinet', + description: 'Source family ', + name: 'fortinet.firewall.srcfamily', + type: 'keyword', + }, + 'fortinet.firewall.srchwvendor': { + category: 'fortinet', + description: 'Source hardware vendor ', + name: 'fortinet.firewall.srchwvendor', + type: 'keyword', + }, + 'fortinet.firewall.srchwversion': { + category: 'fortinet', + description: 'Source hardware version ', + name: 'fortinet.firewall.srchwversion', + type: 'keyword', + }, + 'fortinet.firewall.srcinetsvc': { + category: 'fortinet', + description: 'Source interface service ', + name: 'fortinet.firewall.srcinetsvc', + type: 'keyword', + }, + 'fortinet.firewall.srcname': { + category: 'fortinet', + description: 'Source name ', + name: 'fortinet.firewall.srcname', + type: 'keyword', + }, + 'fortinet.firewall.srcserver': { + category: 'fortinet', + description: 'Source server ', + name: 'fortinet.firewall.srcserver', + type: 'integer', + }, + 'fortinet.firewall.srcssid': { + category: 'fortinet', + description: 'Source SSID ', + name: 'fortinet.firewall.srcssid', + type: 'keyword', + }, + 'fortinet.firewall.srcswversion': { + category: 'fortinet', + description: 'Source software version ', + name: 'fortinet.firewall.srcswversion', + type: 'keyword', + }, + 'fortinet.firewall.srcuuid': { + category: 'fortinet', + description: 'Source UUID ', + name: 'fortinet.firewall.srcuuid', + type: 'keyword', + }, + 'fortinet.firewall.sscname': { + category: 'fortinet', + description: 'SSC name ', + name: 'fortinet.firewall.sscname', + type: 'keyword', + }, + 'fortinet.firewall.ssid': { + category: 'fortinet', + description: 'Base Service Set ID ', + name: 'fortinet.firewall.ssid', + type: 'keyword', + }, + 'fortinet.firewall.sslaction': { + category: 'fortinet', + description: 'SSL Action ', + name: 'fortinet.firewall.sslaction', + type: 'keyword', + }, + 'fortinet.firewall.ssllocal': { + category: 'fortinet', + description: 'WAD SSL local ', + name: 'fortinet.firewall.ssllocal', + type: 'keyword', + }, + 'fortinet.firewall.sslremote': { + category: 'fortinet', + description: 'WAD SSL remote ', + name: 'fortinet.firewall.sslremote', + type: 'keyword', + }, + 'fortinet.firewall.stacount': { + category: 'fortinet', + description: 'Number of stations/clients ', + name: 'fortinet.firewall.stacount', + type: 'integer', + }, + 'fortinet.firewall.stage': { + category: 'fortinet', + description: 'IPSEC stage ', + name: 'fortinet.firewall.stage', + type: 'keyword', + }, + 'fortinet.firewall.stamac': { + category: 'fortinet', + description: '802.1x station mac ', + name: 'fortinet.firewall.stamac', + type: 'keyword', + }, + 'fortinet.firewall.state': { + category: 'fortinet', + description: 'Admin login state ', + name: 'fortinet.firewall.state', + type: 'keyword', + }, + 'fortinet.firewall.status': { + category: 'fortinet', + description: 'Status ', + name: 'fortinet.firewall.status', + type: 'keyword', + }, + 'fortinet.firewall.stitch': { + category: 'fortinet', + description: 'Automation stitch triggered ', + name: 'fortinet.firewall.stitch', + type: 'keyword', + }, + 'fortinet.firewall.subject': { + category: 'fortinet', + description: 'Email subject ', + name: 'fortinet.firewall.subject', + type: 'keyword', + }, + 'fortinet.firewall.submodule': { + category: 'fortinet', + description: 'Configuration Sub-Module Name ', + name: 'fortinet.firewall.submodule', + type: 'keyword', + }, + 'fortinet.firewall.subservice': { + category: 'fortinet', + description: 'AV subservice ', + name: 'fortinet.firewall.subservice', + type: 'keyword', + }, + 'fortinet.firewall.subtype': { + category: 'fortinet', + description: 'Log subtype ', + name: 'fortinet.firewall.subtype', + type: 'keyword', + }, + 'fortinet.firewall.suspicious': { + category: 'fortinet', + description: 'Number of Suspicious MMSs ', + name: 'fortinet.firewall.suspicious', + type: 'integer', + }, + 'fortinet.firewall.switchproto': { + category: 'fortinet', + description: 'Protocol change information ', + name: 'fortinet.firewall.switchproto', + type: 'keyword', + }, + 'fortinet.firewall.sync_status': { + category: 'fortinet', + description: 'The sync status with the master ', + name: 'fortinet.firewall.sync_status', + type: 'keyword', + }, + 'fortinet.firewall.sync_type': { + category: 'fortinet', + description: 'The sync type with the master ', + name: 'fortinet.firewall.sync_type', + type: 'keyword', + }, + 'fortinet.firewall.sysuptime': { + category: 'fortinet', + description: 'System uptime ', + name: 'fortinet.firewall.sysuptime', + type: 'keyword', + }, + 'fortinet.firewall.tamac': { + category: 'fortinet', + description: 'the MAC address of Transmitter, if none, then Receiver ', + name: 'fortinet.firewall.tamac', + type: 'keyword', + }, + 'fortinet.firewall.threattype': { + category: 'fortinet', + description: 'WIDS threat type ', + name: 'fortinet.firewall.threattype', + type: 'keyword', + }, + 'fortinet.firewall.time': { + category: 'fortinet', + description: 'Time of the event ', + name: 'fortinet.firewall.time', + type: 'keyword', + }, + 'fortinet.firewall.to': { + category: 'fortinet', + description: 'Email to field ', + name: 'fortinet.firewall.to', + type: 'keyword', + }, + 'fortinet.firewall.to_vcluster': { + category: 'fortinet', + description: 'destination virtual cluster number ', + name: 'fortinet.firewall.to_vcluster', + type: 'integer', + }, + 'fortinet.firewall.total': { + category: 'fortinet', + description: 'Total memory ', + name: 'fortinet.firewall.total', + type: 'integer', + }, + 'fortinet.firewall.totalsession': { + category: 'fortinet', + description: 'Total Number of Sessions ', + name: 'fortinet.firewall.totalsession', + type: 'integer', + }, + 'fortinet.firewall.trace_id': { + category: 'fortinet', + description: 'Session clash trace ID ', + name: 'fortinet.firewall.trace_id', + type: 'keyword', + }, + 'fortinet.firewall.trandisp': { + category: 'fortinet', + description: 'NAT translation type ', + name: 'fortinet.firewall.trandisp', + type: 'keyword', + }, + 'fortinet.firewall.transid': { + category: 'fortinet', + description: 'HTTP transaction ID ', + name: 'fortinet.firewall.transid', + type: 'integer', + }, + 'fortinet.firewall.translationid': { + category: 'fortinet', + description: 'DNS filter transaltion ID ', + name: 'fortinet.firewall.translationid', + type: 'keyword', + }, + 'fortinet.firewall.trigger': { + category: 'fortinet', + description: 'Automation stitch trigger ', + name: 'fortinet.firewall.trigger', + type: 'keyword', + }, + 'fortinet.firewall.trueclntip': { + category: 'fortinet', + description: 'File filter true client IP ', + name: 'fortinet.firewall.trueclntip', + type: 'ip', + }, + 'fortinet.firewall.tunnelid': { + category: 'fortinet', + description: 'IPSEC tunnel ID ', + name: 'fortinet.firewall.tunnelid', + type: 'integer', + }, + 'fortinet.firewall.tunnelip': { + category: 'fortinet', + description: 'IPSEC tunnel IP ', + name: 'fortinet.firewall.tunnelip', + type: 'ip', + }, + 'fortinet.firewall.tunneltype': { + category: 'fortinet', + description: 'IPSEC tunnel type ', + name: 'fortinet.firewall.tunneltype', + type: 'keyword', + }, + 'fortinet.firewall.type': { + category: 'fortinet', + description: 'Module type ', + name: 'fortinet.firewall.type', + type: 'keyword', + }, + 'fortinet.firewall.ui': { + category: 'fortinet', + description: 'Admin authentication UI type ', + name: 'fortinet.firewall.ui', + type: 'keyword', + }, + 'fortinet.firewall.unauthusersource': { + category: 'fortinet', + description: 'Unauthenticated user source ', + name: 'fortinet.firewall.unauthusersource', + type: 'keyword', + }, + 'fortinet.firewall.unit': { + category: 'fortinet', + description: 'Power supply unit ', + name: 'fortinet.firewall.unit', + type: 'integer', + }, + 'fortinet.firewall.urlfilteridx': { + category: 'fortinet', + description: 'URL filter ID ', + name: 'fortinet.firewall.urlfilteridx', + type: 'integer', + }, + 'fortinet.firewall.urlfilterlist': { + category: 'fortinet', + description: 'URL filter list ', + name: 'fortinet.firewall.urlfilterlist', + type: 'keyword', + }, + 'fortinet.firewall.urlsource': { + category: 'fortinet', + description: 'URL filter source ', + name: 'fortinet.firewall.urlsource', + type: 'keyword', + }, + 'fortinet.firewall.urltype': { + category: 'fortinet', + description: 'URL filter type ', + name: 'fortinet.firewall.urltype', + type: 'keyword', + }, + 'fortinet.firewall.used': { + category: 'fortinet', + description: 'Number of Used IPs ', + name: 'fortinet.firewall.used', + type: 'integer', + }, + 'fortinet.firewall.used_for_type': { + category: 'fortinet', + description: 'Connection for the type ', + name: 'fortinet.firewall.used_for_type', + type: 'integer', + }, + 'fortinet.firewall.utmaction': { + category: 'fortinet', + description: 'Security action performed by UTM ', + name: 'fortinet.firewall.utmaction', + type: 'keyword', + }, + 'fortinet.firewall.vap': { + category: 'fortinet', + description: 'Virtual AP ', + name: 'fortinet.firewall.vap', + type: 'keyword', + }, + 'fortinet.firewall.vapmode': { + category: 'fortinet', + description: 'Virtual AP mode ', + name: 'fortinet.firewall.vapmode', + type: 'keyword', + }, + 'fortinet.firewall.vcluster': { + category: 'fortinet', + description: 'virtual cluster id ', + name: 'fortinet.firewall.vcluster', + type: 'integer', + }, + 'fortinet.firewall.vcluster_member': { + category: 'fortinet', + description: 'Virtual cluster member ', + name: 'fortinet.firewall.vcluster_member', + type: 'integer', + }, + 'fortinet.firewall.vcluster_state': { + category: 'fortinet', + description: 'Virtual cluster state ', + name: 'fortinet.firewall.vcluster_state', + type: 'keyword', + }, + 'fortinet.firewall.vd': { + category: 'fortinet', + description: 'Virtual Domain Name ', + name: 'fortinet.firewall.vd', + type: 'keyword', + }, + 'fortinet.firewall.vdname': { + category: 'fortinet', + description: 'Virtual Domain Name ', + name: 'fortinet.firewall.vdname', + type: 'keyword', + }, + 'fortinet.firewall.vendorurl': { + category: 'fortinet', + description: 'Vulnerability scan vendor name ', + name: 'fortinet.firewall.vendorurl', + type: 'keyword', + }, + 'fortinet.firewall.version': { + category: 'fortinet', + description: 'Version ', + name: 'fortinet.firewall.version', + type: 'keyword', + }, + 'fortinet.firewall.vip': { + category: 'fortinet', + description: 'Virtual IP ', + name: 'fortinet.firewall.vip', + type: 'keyword', + }, + 'fortinet.firewall.virus': { + category: 'fortinet', + description: 'Virus name ', + name: 'fortinet.firewall.virus', + type: 'keyword', + }, + 'fortinet.firewall.virusid': { + category: 'fortinet', + description: 'Virus ID (unique virus identifier) ', + name: 'fortinet.firewall.virusid', + type: 'integer', + }, + 'fortinet.firewall.voip_proto': { + category: 'fortinet', + description: 'VOIP protocol ', + name: 'fortinet.firewall.voip_proto', + type: 'keyword', + }, + 'fortinet.firewall.vpn': { + category: 'fortinet', + description: 'VPN description ', + name: 'fortinet.firewall.vpn', + type: 'keyword', + }, + 'fortinet.firewall.vpntunnel': { + category: 'fortinet', + description: 'IPsec Vpn Tunnel Name ', + name: 'fortinet.firewall.vpntunnel', + type: 'keyword', + }, + 'fortinet.firewall.vpntype': { + category: 'fortinet', + description: 'The type of the VPN tunnel ', + name: 'fortinet.firewall.vpntype', + type: 'keyword', + }, + 'fortinet.firewall.vrf': { + category: 'fortinet', + description: 'VRF number ', + name: 'fortinet.firewall.vrf', + type: 'integer', + }, + 'fortinet.firewall.vulncat': { + category: 'fortinet', + description: 'Vulnerability Category ', + name: 'fortinet.firewall.vulncat', + type: 'keyword', + }, + 'fortinet.firewall.vulnid': { + category: 'fortinet', + description: 'Vulnerability ID ', + name: 'fortinet.firewall.vulnid', + type: 'integer', + }, + 'fortinet.firewall.vulnname': { + category: 'fortinet', + description: 'Vulnerability name ', + name: 'fortinet.firewall.vulnname', + type: 'keyword', + }, + 'fortinet.firewall.vwlid': { + category: 'fortinet', + description: 'VWL ID ', + name: 'fortinet.firewall.vwlid', + type: 'integer', + }, + 'fortinet.firewall.vwlquality': { + category: 'fortinet', + description: 'VWL quality ', + name: 'fortinet.firewall.vwlquality', + type: 'keyword', + }, + 'fortinet.firewall.vwlservice': { + category: 'fortinet', + description: 'VWL service ', + name: 'fortinet.firewall.vwlservice', + type: 'keyword', + }, + 'fortinet.firewall.vwpvlanid': { + category: 'fortinet', + description: 'VWP VLAN ID ', + name: 'fortinet.firewall.vwpvlanid', + type: 'integer', + }, + 'fortinet.firewall.wanin': { + category: 'fortinet', + description: 'WAN incoming traffic in bytes ', + name: 'fortinet.firewall.wanin', + type: 'long', + }, + 'fortinet.firewall.wanoptapptype': { + category: 'fortinet', + description: 'WAN Optimization Application type ', + name: 'fortinet.firewall.wanoptapptype', + type: 'keyword', + }, + 'fortinet.firewall.wanout': { + category: 'fortinet', + description: 'WAN outgoing traffic in bytes ', + name: 'fortinet.firewall.wanout', + type: 'long', + }, + 'fortinet.firewall.weakwepiv': { + category: 'fortinet', + description: 'Weak Wep Initiation Vector ', + name: 'fortinet.firewall.weakwepiv', + type: 'keyword', + }, + 'fortinet.firewall.xauthgroup': { + category: 'fortinet', + description: 'XAuth Group Name ', + name: 'fortinet.firewall.xauthgroup', + type: 'keyword', + }, + 'fortinet.firewall.xauthuser': { + category: 'fortinet', + description: 'XAuth User Name ', + name: 'fortinet.firewall.xauthuser', + type: 'keyword', + }, + 'fortinet.firewall.xid': { + category: 'fortinet', + description: 'Wireless X ID ', + name: 'fortinet.firewall.xid', + type: 'integer', + }, + 'googlecloud.destination.instance.project_id': { + category: 'googlecloud', + description: 'ID of the project containing the VM. ', + name: 'googlecloud.destination.instance.project_id', + type: 'keyword', + }, + 'googlecloud.destination.instance.region': { + category: 'googlecloud', + description: 'Region of the VM. ', + name: 'googlecloud.destination.instance.region', + type: 'keyword', + }, + 'googlecloud.destination.instance.zone': { + category: 'googlecloud', + description: 'Zone of the VM. ', + name: 'googlecloud.destination.instance.zone', + type: 'keyword', + }, + 'googlecloud.destination.vpc.project_id': { + category: 'googlecloud', + description: 'ID of the project containing the VM. ', + name: 'googlecloud.destination.vpc.project_id', + type: 'keyword', + }, + 'googlecloud.destination.vpc.vpc_name': { + category: 'googlecloud', + description: 'VPC on which the VM is operating. ', + name: 'googlecloud.destination.vpc.vpc_name', + type: 'keyword', + }, + 'googlecloud.destination.vpc.subnetwork_name': { + category: 'googlecloud', + description: 'Subnetwork on which the VM is operating. ', + name: 'googlecloud.destination.vpc.subnetwork_name', + type: 'keyword', + }, + 'googlecloud.source.instance.project_id': { + category: 'googlecloud', + description: 'ID of the project containing the VM. ', + name: 'googlecloud.source.instance.project_id', + type: 'keyword', + }, + 'googlecloud.source.instance.region': { + category: 'googlecloud', + description: 'Region of the VM. ', + name: 'googlecloud.source.instance.region', + type: 'keyword', + }, + 'googlecloud.source.instance.zone': { + category: 'googlecloud', + description: 'Zone of the VM. ', + name: 'googlecloud.source.instance.zone', + type: 'keyword', + }, + 'googlecloud.source.vpc.project_id': { + category: 'googlecloud', + description: 'ID of the project containing the VM. ', + name: 'googlecloud.source.vpc.project_id', + type: 'keyword', + }, + 'googlecloud.source.vpc.vpc_name': { + category: 'googlecloud', + description: 'VPC on which the VM is operating. ', + name: 'googlecloud.source.vpc.vpc_name', + type: 'keyword', + }, + 'googlecloud.source.vpc.subnetwork_name': { + category: 'googlecloud', + description: 'Subnetwork on which the VM is operating. ', + name: 'googlecloud.source.vpc.subnetwork_name', + type: 'keyword', + }, + 'googlecloud.audit.type': { + category: 'googlecloud', + description: 'Type property. ', + name: 'googlecloud.audit.type', + type: 'keyword', + }, + 'googlecloud.audit.authentication_info.principal_email': { + category: 'googlecloud', + description: 'The email address of the authenticated user making the request. ', + name: 'googlecloud.audit.authentication_info.principal_email', + type: 'keyword', + }, + 'googlecloud.audit.authentication_info.authority_selector': { + category: 'googlecloud', + description: + 'The authority selector specified by the requestor, if any. It is not guaranteed that the principal was allowed to use this authority. ', + name: 'googlecloud.audit.authentication_info.authority_selector', + type: 'keyword', + }, + 'googlecloud.audit.authorization_info.permission': { + category: 'googlecloud', + description: 'The required IAM permission. ', + name: 'googlecloud.audit.authorization_info.permission', + type: 'keyword', + }, + 'googlecloud.audit.authorization_info.granted': { + category: 'googlecloud', + description: 'Whether or not authorization for resource and permission was granted. ', + name: 'googlecloud.audit.authorization_info.granted', + type: 'boolean', + }, + 'googlecloud.audit.authorization_info.resource_attributes.service': { + category: 'googlecloud', + description: 'The name of the service. ', + name: 'googlecloud.audit.authorization_info.resource_attributes.service', + type: 'keyword', + }, + 'googlecloud.audit.authorization_info.resource_attributes.name': { + category: 'googlecloud', + description: 'The name of the resource. ', + name: 'googlecloud.audit.authorization_info.resource_attributes.name', + type: 'keyword', + }, + 'googlecloud.audit.authorization_info.resource_attributes.type': { + category: 'googlecloud', + description: 'The type of the resource. ', + name: 'googlecloud.audit.authorization_info.resource_attributes.type', + type: 'keyword', + }, + 'googlecloud.audit.method_name': { + category: 'googlecloud', + description: + "The name of the service method or operation. For API calls, this should be the name of the API method. For example, 'google.datastore.v1.Datastore.RunQuery'. ", + name: 'googlecloud.audit.method_name', + type: 'keyword', + }, + 'googlecloud.audit.num_response_items': { + category: 'googlecloud', + description: 'The number of items returned from a List or Query API method, if applicable. ', + name: 'googlecloud.audit.num_response_items', + type: 'long', + }, + 'googlecloud.audit.request.proto_name': { + category: 'googlecloud', + description: 'Type property of the request. ', + name: 'googlecloud.audit.request.proto_name', + type: 'keyword', + }, + 'googlecloud.audit.request.filter': { + category: 'googlecloud', + description: 'Filter of the request. ', + name: 'googlecloud.audit.request.filter', + type: 'keyword', + }, + 'googlecloud.audit.request.name': { + category: 'googlecloud', + description: 'Name of the request. ', + name: 'googlecloud.audit.request.name', + type: 'keyword', + }, + 'googlecloud.audit.request.resource_name': { + category: 'googlecloud', + description: 'Name of the request resource. ', + name: 'googlecloud.audit.request.resource_name', + type: 'keyword', + }, + 'googlecloud.audit.request_metadata.caller_ip': { + category: 'googlecloud', + description: 'The IP address of the caller. ', + name: 'googlecloud.audit.request_metadata.caller_ip', + type: 'ip', + }, + 'googlecloud.audit.request_metadata.caller_supplied_user_agent': { + category: 'googlecloud', + description: + 'The user agent of the caller. This information is not authenticated and should be treated accordingly. ', + name: 'googlecloud.audit.request_metadata.caller_supplied_user_agent', + type: 'keyword', + }, + 'googlecloud.audit.response.proto_name': { + category: 'googlecloud', + description: 'Type property of the response. ', + name: 'googlecloud.audit.response.proto_name', + type: 'keyword', + }, + 'googlecloud.audit.response.details.group': { + category: 'googlecloud', + description: 'The name of the group. ', + name: 'googlecloud.audit.response.details.group', + type: 'keyword', + }, + 'googlecloud.audit.response.details.kind': { + category: 'googlecloud', + description: 'The kind of the response details. ', + name: 'googlecloud.audit.response.details.kind', + type: 'keyword', + }, + 'googlecloud.audit.response.details.name': { + category: 'googlecloud', + description: 'The name of the response details. ', + name: 'googlecloud.audit.response.details.name', + type: 'keyword', + }, + 'googlecloud.audit.response.details.uid': { + category: 'googlecloud', + description: 'The uid of the response details. ', + name: 'googlecloud.audit.response.details.uid', + type: 'keyword', + }, + 'googlecloud.audit.response.status': { + category: 'googlecloud', + description: 'Status of the response. ', + name: 'googlecloud.audit.response.status', + type: 'keyword', + }, + 'googlecloud.audit.resource_name': { + category: 'googlecloud', + description: + "The resource or collection that is the target of the operation. The name is a scheme-less URI, not including the API service name. For example, 'shelves/SHELF_ID/books'. ", + name: 'googlecloud.audit.resource_name', + type: 'keyword', + }, + 'googlecloud.audit.resource_location.current_locations': { + category: 'googlecloud', + description: 'Current locations of the resource. ', + name: 'googlecloud.audit.resource_location.current_locations', + type: 'keyword', + }, + 'googlecloud.audit.service_name': { + category: 'googlecloud', + description: + 'The name of the API service performing the operation. For example, datastore.googleapis.com. ', + name: 'googlecloud.audit.service_name', + type: 'keyword', + }, + 'googlecloud.audit.status.code': { + category: 'googlecloud', + description: 'The status code, which should be an enum value of google.rpc.Code. ', + name: 'googlecloud.audit.status.code', + type: 'integer', + }, + 'googlecloud.audit.status.message': { + category: 'googlecloud', + description: + 'A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client. ', + name: 'googlecloud.audit.status.message', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.priority': { + category: 'googlecloud', + description: 'The priority for the firewall rule.', + name: 'googlecloud.firewall.rule_details.priority', + type: 'long', + }, + 'googlecloud.firewall.rule_details.action': { + category: 'googlecloud', + description: 'Action that the rule performs on match.', + name: 'googlecloud.firewall.rule_details.action', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.direction': { + category: 'googlecloud', + description: 'Direction of traffic that matches this rule.', + name: 'googlecloud.firewall.rule_details.direction', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.reference': { + category: 'googlecloud', + description: 'Reference to the firewall rule.', + name: 'googlecloud.firewall.rule_details.reference', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.source_range': { + category: 'googlecloud', + description: 'List of source ranges that the firewall rule applies to.', + name: 'googlecloud.firewall.rule_details.source_range', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.destination_range': { + category: 'googlecloud', + description: 'List of destination ranges that the firewall applies to.', + name: 'googlecloud.firewall.rule_details.destination_range', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.source_tag': { + category: 'googlecloud', + description: 'List of all the source tags that the firewall rule applies to. ', + name: 'googlecloud.firewall.rule_details.source_tag', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.target_tag': { + category: 'googlecloud', + description: 'List of all the target tags that the firewall rule applies to. ', + name: 'googlecloud.firewall.rule_details.target_tag', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.ip_port_info': { + category: 'googlecloud', + description: 'List of ip protocols and applicable port ranges for rules. ', + name: 'googlecloud.firewall.rule_details.ip_port_info', + type: 'array', + }, + 'googlecloud.firewall.rule_details.source_service_account': { + category: 'googlecloud', + description: 'List of all the source service accounts that the firewall rule applies to. ', + name: 'googlecloud.firewall.rule_details.source_service_account', + type: 'keyword', + }, + 'googlecloud.firewall.rule_details.target_service_account': { + category: 'googlecloud', + description: 'List of all the target service accounts that the firewall rule applies to. ', + name: 'googlecloud.firewall.rule_details.target_service_account', + type: 'keyword', + }, + 'googlecloud.vpcflow.reporter': { + category: 'googlecloud', + description: "The side which reported the flow. Can be either 'SRC' or 'DEST'. ", + name: 'googlecloud.vpcflow.reporter', + type: 'keyword', + }, + 'googlecloud.vpcflow.rtt.ms': { + category: 'googlecloud', + description: + 'Latency as measured (for TCP flows only) during the time interval. This is the time elapsed between sending a SEQ and receiving a corresponding ACK and it contains the network RTT as well as the application related delay. ', + name: 'googlecloud.vpcflow.rtt.ms', + type: 'long', + }, + 'gsuite.actor.type': { + category: 'gsuite', + description: + 'The type of actor. Values can be: *USER*: Another user in the same domain. *EXTERNAL_USER*: A user outside the domain. *KEY*: A non-human actor. ', + name: 'gsuite.actor.type', + type: 'keyword', + }, + 'gsuite.actor.key': { + category: 'gsuite', + description: + 'Only present when `actor.type` is `KEY`. Can be the `consumer_key` of the requestor for OAuth 2LO API requests or an identifier for robot accounts. ', + name: 'gsuite.actor.key', + type: 'keyword', + }, + 'gsuite.event.type': { + category: 'gsuite', + description: + 'The type of GSuite event, mapped from `items[].events[].type` in the original payload. Each fileset can have a different set of values for it, more details can be found at https://developers.google.com/admin-sdk/reports/v1/reference/activities/list ', + example: 'audit#activity', + name: 'gsuite.event.type', + type: 'keyword', + }, + 'gsuite.kind': { + category: 'gsuite', + description: + 'The type of API resource, mapped from `kind` in the original payload. More details can be found at https://developers.google.com/admin-sdk/reports/v1/reference/activities/list ', + example: 'audit#activity', + name: 'gsuite.kind', + type: 'keyword', + }, + 'gsuite.organization.domain': { + category: 'gsuite', + description: "The domain that is affected by the report's event. ", + name: 'gsuite.organization.domain', + type: 'keyword', + }, + 'gsuite.admin.application.edition': { + category: 'gsuite', + description: 'The GSuite edition.', + name: 'gsuite.admin.application.edition', + type: 'keyword', + }, + 'gsuite.admin.application.name': { + category: 'gsuite', + description: "The application's name.", + name: 'gsuite.admin.application.name', + type: 'keyword', + }, + 'gsuite.admin.application.enabled': { + category: 'gsuite', + description: 'The enabled application.', + name: 'gsuite.admin.application.enabled', + type: 'keyword', + }, + 'gsuite.admin.application.licences_order_number': { + category: 'gsuite', + description: 'Order number used to redeem licenses.', + name: 'gsuite.admin.application.licences_order_number', + type: 'keyword', + }, + 'gsuite.admin.application.licences_purchased': { + category: 'gsuite', + description: 'Number of licences purchased.', + name: 'gsuite.admin.application.licences_purchased', + type: 'keyword', + }, + 'gsuite.admin.application.id': { + category: 'gsuite', + description: 'The application ID.', + name: 'gsuite.admin.application.id', + type: 'keyword', + }, + 'gsuite.admin.application.asp_id': { + category: 'gsuite', + description: 'The application specific password ID.', + name: 'gsuite.admin.application.asp_id', + type: 'keyword', + }, + 'gsuite.admin.application.package_id': { + category: 'gsuite', + description: 'The mobile application package ID.', + name: 'gsuite.admin.application.package_id', + type: 'keyword', + }, + 'gsuite.admin.group.email': { + category: 'gsuite', + description: "The group's primary email address.", + name: 'gsuite.admin.group.email', + type: 'keyword', + }, + 'gsuite.admin.new_value': { + category: 'gsuite', + description: 'The new value for the setting.', + name: 'gsuite.admin.new_value', + type: 'keyword', + }, + 'gsuite.admin.old_value': { + category: 'gsuite', + description: 'The old value for the setting.', + name: 'gsuite.admin.old_value', + type: 'keyword', + }, + 'gsuite.admin.org_unit.name': { + category: 'gsuite', + description: 'The organizational unit name.', + name: 'gsuite.admin.org_unit.name', + type: 'keyword', + }, + 'gsuite.admin.org_unit.full': { + category: 'gsuite', + description: 'The org unit full path including the root org unit name.', + name: 'gsuite.admin.org_unit.full', + type: 'keyword', + }, + 'gsuite.admin.setting.name': { + category: 'gsuite', + description: 'The setting name.', + name: 'gsuite.admin.setting.name', + type: 'keyword', + }, + 'gsuite.admin.user_defined_setting.name': { + category: 'gsuite', + description: 'The name of the user-defined setting.', + name: 'gsuite.admin.user_defined_setting.name', + type: 'keyword', + }, + 'gsuite.admin.setting.description': { + category: 'gsuite', + description: 'The setting name.', + name: 'gsuite.admin.setting.description', + type: 'keyword', + }, + 'gsuite.admin.group.priorities': { + category: 'gsuite', + description: 'Group priorities.', + name: 'gsuite.admin.group.priorities', + type: 'keyword', + }, + 'gsuite.admin.domain.alias': { + category: 'gsuite', + description: 'The domain alias.', + name: 'gsuite.admin.domain.alias', + type: 'keyword', + }, + 'gsuite.admin.domain.name': { + category: 'gsuite', + description: 'The primary domain name.', + name: 'gsuite.admin.domain.name', + type: 'keyword', + }, + 'gsuite.admin.domain.secondary_name': { + category: 'gsuite', + description: 'The secondary domain name.', + name: 'gsuite.admin.domain.secondary_name', + type: 'keyword', + }, + 'gsuite.admin.managed_configuration': { + category: 'gsuite', + description: 'The name of the managed configuration.', + name: 'gsuite.admin.managed_configuration', + type: 'keyword', + }, + 'gsuite.admin.non_featured_services_selection': { + category: 'gsuite', + description: + 'Non-featured services selection. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-application-settings#FLASHLIGHT_EDU_NON_FEATURED_SERVICES_SELECTED ', + name: 'gsuite.admin.non_featured_services_selection', + type: 'keyword', + }, + 'gsuite.admin.field': { + category: 'gsuite', + description: 'The name of the field.', + name: 'gsuite.admin.field', + type: 'keyword', + }, + 'gsuite.admin.resource.id': { + category: 'gsuite', + description: 'The name of the resource identifier.', + name: 'gsuite.admin.resource.id', + type: 'keyword', + }, + 'gsuite.admin.user.email': { + category: 'gsuite', + description: "The user's primary email address.", + name: 'gsuite.admin.user.email', + type: 'keyword', + }, + 'gsuite.admin.user.nickname': { + category: 'gsuite', + description: "The user's nickname.", + name: 'gsuite.admin.user.nickname', + type: 'keyword', + }, + 'gsuite.admin.user.birthdate': { + category: 'gsuite', + description: "The user's birth date.", + name: 'gsuite.admin.user.birthdate', + type: 'date', + }, + 'gsuite.admin.gateway.name': { + category: 'gsuite', + description: 'Gateway name. Present on some chat settings.', + name: 'gsuite.admin.gateway.name', + type: 'keyword', + }, + 'gsuite.admin.chrome_os.session_type': { + category: 'gsuite', + description: 'Chrome OS session type.', + name: 'gsuite.admin.chrome_os.session_type', + type: 'keyword', + }, + 'gsuite.admin.device.serial_number': { + category: 'gsuite', + description: 'Device serial number.', + name: 'gsuite.admin.device.serial_number', + type: 'keyword', + }, + 'gsuite.admin.device.id': { + category: 'gsuite', + name: 'gsuite.admin.device.id', + type: 'keyword', + }, + 'gsuite.admin.device.type': { + category: 'gsuite', + description: 'Device type.', + name: 'gsuite.admin.device.type', + type: 'keyword', + }, + 'gsuite.admin.print_server.name': { + category: 'gsuite', + description: 'The name of the print server.', + name: 'gsuite.admin.print_server.name', + type: 'keyword', + }, + 'gsuite.admin.printer.name': { + category: 'gsuite', + description: 'The name of the printer.', + name: 'gsuite.admin.printer.name', + type: 'keyword', + }, + 'gsuite.admin.device.command_details': { + category: 'gsuite', + description: 'Command details.', + name: 'gsuite.admin.device.command_details', + type: 'keyword', + }, + 'gsuite.admin.role.id': { + category: 'gsuite', + description: 'Unique identifier for this role privilege.', + name: 'gsuite.admin.role.id', + type: 'keyword', + }, + 'gsuite.admin.role.name': { + category: 'gsuite', + description: + 'The role name. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-delegated-admin-settings ', + name: 'gsuite.admin.role.name', + type: 'keyword', + }, + 'gsuite.admin.privilege.name': { + category: 'gsuite', + description: 'Privilege name.', + name: 'gsuite.admin.privilege.name', + type: 'keyword', + }, + 'gsuite.admin.service.name': { + category: 'gsuite', + description: 'The service name.', + name: 'gsuite.admin.service.name', + type: 'keyword', + }, + 'gsuite.admin.url.name': { + category: 'gsuite', + description: 'The website name.', + name: 'gsuite.admin.url.name', + type: 'keyword', + }, + 'gsuite.admin.product.name': { + category: 'gsuite', + description: 'The product name.', + name: 'gsuite.admin.product.name', + type: 'keyword', + }, + 'gsuite.admin.product.sku': { + category: 'gsuite', + description: 'The product SKU.', + name: 'gsuite.admin.product.sku', + type: 'keyword', + }, + 'gsuite.admin.bulk_upload.failed': { + category: 'gsuite', + description: 'Number of failed records in bulk upload operation.', + name: 'gsuite.admin.bulk_upload.failed', + type: 'long', + }, + 'gsuite.admin.bulk_upload.total': { + category: 'gsuite', + description: 'Number of total records in bulk upload operation.', + name: 'gsuite.admin.bulk_upload.total', + type: 'long', + }, + 'gsuite.admin.group.allowed_list': { + category: 'gsuite', + description: 'Names of allow-listed groups.', + name: 'gsuite.admin.group.allowed_list', + type: 'keyword', + }, + 'gsuite.admin.email.quarantine_name': { + category: 'gsuite', + description: 'The name of the quarantine.', + name: 'gsuite.admin.email.quarantine_name', + type: 'keyword', + }, + 'gsuite.admin.email.log_search_filter.message_id': { + category: 'gsuite', + description: "The log search filter's email message ID.", + name: 'gsuite.admin.email.log_search_filter.message_id', + type: 'keyword', + }, + 'gsuite.admin.email.log_search_filter.start_date': { + category: 'gsuite', + description: "The log search filter's start date.", + name: 'gsuite.admin.email.log_search_filter.start_date', + type: 'date', + }, + 'gsuite.admin.email.log_search_filter.end_date': { + category: 'gsuite', + description: "The log search filter's ending date.", + name: 'gsuite.admin.email.log_search_filter.end_date', + type: 'date', + }, + 'gsuite.admin.email.log_search_filter.recipient.value': { + category: 'gsuite', + description: "The log search filter's email recipient.", + name: 'gsuite.admin.email.log_search_filter.recipient.value', + type: 'keyword', + }, + 'gsuite.admin.email.log_search_filter.sender.value': { + category: 'gsuite', + description: "The log search filter's email sender.", + name: 'gsuite.admin.email.log_search_filter.sender.value', + type: 'keyword', + }, + 'gsuite.admin.email.log_search_filter.recipient.ip': { + category: 'gsuite', + description: "The log search filter's email recipient's IP address.", + name: 'gsuite.admin.email.log_search_filter.recipient.ip', + type: 'ip', + }, + 'gsuite.admin.email.log_search_filter.sender.ip': { + category: 'gsuite', + description: "The log search filter's email sender's IP address.", + name: 'gsuite.admin.email.log_search_filter.sender.ip', + type: 'ip', + }, + 'gsuite.admin.chrome_licenses.enabled': { + category: 'gsuite', + description: + 'Licences enabled. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-org-settings ', + name: 'gsuite.admin.chrome_licenses.enabled', + type: 'keyword', + }, + 'gsuite.admin.chrome_licenses.allowed': { + category: 'gsuite', + description: + 'Licences enabled. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-org-settings ', + name: 'gsuite.admin.chrome_licenses.allowed', + type: 'keyword', + }, + 'gsuite.admin.oauth2.service.name': { + category: 'gsuite', + description: + 'OAuth2 service name. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-security-settings ', + name: 'gsuite.admin.oauth2.service.name', + type: 'keyword', + }, + 'gsuite.admin.oauth2.application.id': { + category: 'gsuite', + description: 'OAuth2 application ID.', + name: 'gsuite.admin.oauth2.application.id', + type: 'keyword', + }, + 'gsuite.admin.oauth2.application.name': { + category: 'gsuite', + description: 'OAuth2 application name.', + name: 'gsuite.admin.oauth2.application.name', + type: 'keyword', + }, + 'gsuite.admin.oauth2.application.type': { + category: 'gsuite', + description: + 'OAuth2 application type. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-security-settings ', + name: 'gsuite.admin.oauth2.application.type', + type: 'keyword', + }, + 'gsuite.admin.verification_method': { + category: 'gsuite', + description: + 'Related verification method. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-security-settings and https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-domain-settings ', + name: 'gsuite.admin.verification_method', + type: 'keyword', + }, + 'gsuite.admin.alert.name': { + category: 'gsuite', + description: 'The alert name.', + name: 'gsuite.admin.alert.name', + type: 'keyword', + }, + 'gsuite.admin.rule.name': { + category: 'gsuite', + description: 'The rule name.', + name: 'gsuite.admin.rule.name', + type: 'keyword', + }, + 'gsuite.admin.api.client.name': { + category: 'gsuite', + description: 'The API client name.', + name: 'gsuite.admin.api.client.name', + type: 'keyword', + }, + 'gsuite.admin.api.scopes': { + category: 'gsuite', + description: 'The API scopes.', + name: 'gsuite.admin.api.scopes', + type: 'keyword', + }, + 'gsuite.admin.mdm.token': { + category: 'gsuite', + description: 'The MDM vendor enrollment token.', + name: 'gsuite.admin.mdm.token', + type: 'keyword', + }, + 'gsuite.admin.mdm.vendor': { + category: 'gsuite', + description: "The MDM vendor's name.", + name: 'gsuite.admin.mdm.vendor', + type: 'keyword', + }, + 'gsuite.admin.info_type': { + category: 'gsuite', + description: + 'This will be used to state what kind of information was changed. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-domain-settings ', + name: 'gsuite.admin.info_type', + type: 'keyword', + }, + 'gsuite.admin.email_monitor.dest_email': { + category: 'gsuite', + description: 'The destination address of the email monitor.', + name: 'gsuite.admin.email_monitor.dest_email', + type: 'keyword', + }, + 'gsuite.admin.email_monitor.level.chat': { + category: 'gsuite', + description: 'The chat email monitor level.', + name: 'gsuite.admin.email_monitor.level.chat', + type: 'keyword', + }, + 'gsuite.admin.email_monitor.level.draft': { + category: 'gsuite', + description: 'The draft email monitor level.', + name: 'gsuite.admin.email_monitor.level.draft', + type: 'keyword', + }, + 'gsuite.admin.email_monitor.level.incoming': { + category: 'gsuite', + description: 'The incoming email monitor level.', + name: 'gsuite.admin.email_monitor.level.incoming', + type: 'keyword', + }, + 'gsuite.admin.email_monitor.level.outgoing': { + category: 'gsuite', + description: 'The outgoing email monitor level.', + name: 'gsuite.admin.email_monitor.level.outgoing', + type: 'keyword', + }, + 'gsuite.admin.email_dump.include_deleted': { + category: 'gsuite', + description: 'Indicates if deleted emails are included in the export.', + name: 'gsuite.admin.email_dump.include_deleted', + type: 'boolean', + }, + 'gsuite.admin.email_dump.package_content': { + category: 'gsuite', + description: 'The contents of the mailbox package.', + name: 'gsuite.admin.email_dump.package_content', + type: 'keyword', + }, + 'gsuite.admin.email_dump.query': { + category: 'gsuite', + description: 'The search query used for the dump.', + name: 'gsuite.admin.email_dump.query', + type: 'keyword', + }, + 'gsuite.admin.request.id': { + category: 'gsuite', + description: 'The request ID.', + name: 'gsuite.admin.request.id', + type: 'keyword', + }, + 'gsuite.admin.mobile.action.id': { + category: 'gsuite', + description: "The mobile device action's ID.", + name: 'gsuite.admin.mobile.action.id', + type: 'keyword', + }, + 'gsuite.admin.mobile.action.type': { + category: 'gsuite', + description: + "The mobile device action's type. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-mobile-settings ", + name: 'gsuite.admin.mobile.action.type', + type: 'keyword', + }, + 'gsuite.admin.mobile.certificate.name': { + category: 'gsuite', + description: 'The mobile certificate common name.', + name: 'gsuite.admin.mobile.certificate.name', + type: 'keyword', + }, + 'gsuite.admin.mobile.company_owned_devices': { + category: 'gsuite', + description: 'The number of devices a company owns.', + name: 'gsuite.admin.mobile.company_owned_devices', + type: 'long', + }, + 'gsuite.admin.distribution.entity.name': { + category: 'gsuite', + description: + 'The distribution entity value, which can be a group name or an org-unit name. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-mobile-settings ', + name: 'gsuite.admin.distribution.entity.name', + type: 'keyword', + }, + 'gsuite.admin.distribution.entity.type': { + category: 'gsuite', + description: + 'The distribution entity type, which can be a group or an org-unit. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/admin-mobile-settings ', + name: 'gsuite.admin.distribution.entity.type', + type: 'keyword', + }, + 'gsuite.drive.billable': { + category: 'gsuite', + description: 'Whether this activity is billable.', + name: 'gsuite.drive.billable', + type: 'boolean', + }, + 'gsuite.drive.source_folder_id': { + category: 'gsuite', + name: 'gsuite.drive.source_folder_id', + type: 'keyword', + }, + 'gsuite.drive.source_folder_title': { + category: 'gsuite', + name: 'gsuite.drive.source_folder_title', + type: 'keyword', + }, + 'gsuite.drive.destination_folder_id': { + category: 'gsuite', + name: 'gsuite.drive.destination_folder_id', + type: 'keyword', + }, + 'gsuite.drive.destination_folder_title': { + category: 'gsuite', + name: 'gsuite.drive.destination_folder_title', + type: 'keyword', + }, + 'gsuite.drive.file.id': { + category: 'gsuite', + name: 'gsuite.drive.file.id', + type: 'keyword', + }, + 'gsuite.drive.file.type': { + category: 'gsuite', + description: + 'Document Drive type. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive ', + name: 'gsuite.drive.file.type', + type: 'keyword', + }, + 'gsuite.drive.originating_app_id': { + category: 'gsuite', + description: 'The Google Cloud Project ID of the application that performed the action. ', + name: 'gsuite.drive.originating_app_id', + type: 'keyword', + }, + 'gsuite.drive.file.owner.email': { + category: 'gsuite', + name: 'gsuite.drive.file.owner.email', + type: 'keyword', + }, + 'gsuite.drive.file.owner.is_shared_drive': { + category: 'gsuite', + description: 'Boolean flag denoting whether owner is a shared drive. ', + name: 'gsuite.drive.file.owner.is_shared_drive', + type: 'boolean', + }, + 'gsuite.drive.primary_event': { + category: 'gsuite', + description: + 'Whether this is a primary event. A single user action in Drive may generate several events. ', + name: 'gsuite.drive.primary_event', + type: 'boolean', + }, + 'gsuite.drive.shared_drive_id': { + category: 'gsuite', + description: + 'The unique identifier of the Team Drive. Only populated for for events relating to a Team Drive or item contained inside a Team Drive. ', + name: 'gsuite.drive.shared_drive_id', + type: 'keyword', + }, + 'gsuite.drive.visibility': { + category: 'gsuite', + description: + 'Visibility of target file. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive ', + name: 'gsuite.drive.visibility', + type: 'keyword', + }, + 'gsuite.drive.new_value': { + category: 'gsuite', + description: + 'When a setting or property of the file changes, the new value for it will appear here. ', + name: 'gsuite.drive.new_value', + type: 'keyword', + }, + 'gsuite.drive.old_value': { + category: 'gsuite', + description: + 'When a setting or property of the file changes, the old value for it will appear here. ', + name: 'gsuite.drive.old_value', + type: 'keyword', + }, + 'gsuite.drive.sheets_import_range_recipient_doc': { + category: 'gsuite', + description: 'Doc ID of the recipient of a sheets import range.', + name: 'gsuite.drive.sheets_import_range_recipient_doc', + type: 'keyword', + }, + 'gsuite.drive.old_visibility': { + category: 'gsuite', + description: 'When visibility changes, this holds the old value. ', + name: 'gsuite.drive.old_visibility', + type: 'keyword', + }, + 'gsuite.drive.visibility_change': { + category: 'gsuite', + description: 'When visibility changes, this holds the new overall visibility of the file. ', + name: 'gsuite.drive.visibility_change', + type: 'keyword', + }, + 'gsuite.drive.target_domain': { + category: 'gsuite', + description: + 'The domain for which the acccess scope was changed. This can also be the alias all to indicate the access scope was changed for all domains that have visibility for this document. ', + name: 'gsuite.drive.target_domain', + type: 'keyword', + }, + 'gsuite.drive.added_role': { + category: 'gsuite', + description: + 'Added membership role of a user/group in a Team Drive. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive ', + name: 'gsuite.drive.added_role', + type: 'keyword', + }, + 'gsuite.drive.membership_change_type': { + category: 'gsuite', + description: + 'Type of change in Team Drive membership of a user/group. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive ', + name: 'gsuite.drive.membership_change_type', + type: 'keyword', + }, + 'gsuite.drive.shared_drive_settings_change_type': { + category: 'gsuite', + description: + 'Type of change in Team Drive settings. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive ', + name: 'gsuite.drive.shared_drive_settings_change_type', + type: 'keyword', + }, + 'gsuite.drive.removed_role': { + category: 'gsuite', + description: + 'Removed membership role of a user/group in a Team Drive. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/drive ', + name: 'gsuite.drive.removed_role', + type: 'keyword', + }, + 'gsuite.drive.target': { + category: 'gsuite', + description: 'Target user or group.', + name: 'gsuite.drive.target', + type: 'keyword', + }, + 'gsuite.groups.acl_permission': { + category: 'gsuite', + description: + 'Group permission setting updated. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/groups ', + name: 'gsuite.groups.acl_permission', + type: 'keyword', + }, + 'gsuite.groups.email': { + category: 'gsuite', + description: 'Group email. ', + name: 'gsuite.groups.email', + type: 'keyword', + }, + 'gsuite.groups.member.email': { + category: 'gsuite', + description: 'Member email. ', + name: 'gsuite.groups.member.email', + type: 'keyword', + }, + 'gsuite.groups.member.role': { + category: 'gsuite', + description: + 'Member role. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/groups ', + name: 'gsuite.groups.member.role', + type: 'keyword', + }, + 'gsuite.groups.setting': { + category: 'gsuite', + description: + 'Group setting updated. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/groups ', + name: 'gsuite.groups.setting', + type: 'keyword', + }, + 'gsuite.groups.new_value': { + category: 'gsuite', + description: + 'New value(s) of the group setting. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/groups ', + name: 'gsuite.groups.new_value', + type: 'keyword', + }, + 'gsuite.groups.old_value': { + category: 'gsuite', + description: + 'Old value(s) of the group setting. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/groups', + name: 'gsuite.groups.old_value', + type: 'keyword', + }, + 'gsuite.groups.value': { + category: 'gsuite', + description: + 'Value of the group setting. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/groups ', + name: 'gsuite.groups.value', + type: 'keyword', + }, + 'gsuite.groups.message.id': { + category: 'gsuite', + description: 'SMTP message Id of an email message. Present for moderation events. ', + name: 'gsuite.groups.message.id', + type: 'keyword', + }, + 'gsuite.groups.message.moderation_action': { + category: 'gsuite', + description: 'Message moderation action. Possible values are `approved` and `rejected`. ', + name: 'gsuite.groups.message.moderation_action', + type: 'keyword', + }, + 'gsuite.groups.status': { + category: 'gsuite', + description: + 'A status describing the output of an operation. Possible values are `failed` and `succeeded`. ', + name: 'gsuite.groups.status', + type: 'keyword', + }, + 'gsuite.login.affected_email_address': { + category: 'gsuite', + name: 'gsuite.login.affected_email_address', + type: 'keyword', + }, + 'gsuite.login.challenge_method': { + category: 'gsuite', + description: + 'Login challenge method. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/login. ', + name: 'gsuite.login.challenge_method', + type: 'keyword', + }, + 'gsuite.login.failure_type': { + category: 'gsuite', + description: + 'Login failure type. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/login. ', + name: 'gsuite.login.failure_type', + type: 'keyword', + }, + 'gsuite.login.type': { + category: 'gsuite', + description: + 'Login credentials type. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/login. ', + name: 'gsuite.login.type', + type: 'keyword', + }, + 'gsuite.login.is_second_factor': { + category: 'gsuite', + name: 'gsuite.login.is_second_factor', + type: 'boolean', + }, + 'gsuite.login.is_suspicious': { + category: 'gsuite', + name: 'gsuite.login.is_suspicious', + type: 'boolean', + }, + 'gsuite.saml.application_name': { + category: 'gsuite', + description: 'Saml SP application name. ', + name: 'gsuite.saml.application_name', + type: 'keyword', + }, + 'gsuite.saml.failure_type': { + category: 'gsuite', + description: + 'Login failure type. For a list of possible values refer to https://developers.google.com/admin-sdk/reports/v1/appendix/activity/saml. ', + name: 'gsuite.saml.failure_type', + type: 'keyword', + }, + 'gsuite.saml.initiated_by': { + category: 'gsuite', + description: 'Requester of SAML authentication. ', + name: 'gsuite.saml.initiated_by', + type: 'keyword', + }, + 'gsuite.saml.orgunit_path': { + category: 'gsuite', + description: 'User orgunit. ', + name: 'gsuite.saml.orgunit_path', + type: 'keyword', + }, + 'gsuite.saml.status_code': { + category: 'gsuite', + description: 'SAML status code. ', + name: 'gsuite.saml.status_code', + type: 'long', + }, + 'gsuite.saml.second_level_status_code': { + category: 'gsuite', + description: 'SAML second level status code. ', + name: 'gsuite.saml.second_level_status_code', + type: 'long', + }, + 'ibmmq.errorlog.installation': { + category: 'ibmmq', + description: + 'This is the installation name which can be given at installation time. Each installation of IBM MQ on UNIX, Linux, and Windows, has a unique identifier known as an installation name. The installation name is used to associate things such as queue managers and configuration files with an installation. ', + name: 'ibmmq.errorlog.installation', + type: 'keyword', + }, + 'ibmmq.errorlog.qmgr': { + category: 'ibmmq', + description: + 'Name of the queue manager. Queue managers provide queuing services to applications, and manages the queues that belong to them. ', + name: 'ibmmq.errorlog.qmgr', + type: 'keyword', + }, + 'ibmmq.errorlog.arithinsert': { + category: 'ibmmq', + description: 'Changing content based on error.id', + name: 'ibmmq.errorlog.arithinsert', + type: 'keyword', + }, + 'ibmmq.errorlog.commentinsert': { + category: 'ibmmq', + description: 'Changing content based on error.id', + name: 'ibmmq.errorlog.commentinsert', + type: 'keyword', + }, + 'ibmmq.errorlog.errordescription': { + category: 'ibmmq', + description: 'Please add description', + example: 'Please add example', + name: 'ibmmq.errorlog.errordescription', + type: 'text', + }, + 'ibmmq.errorlog.explanation': { + category: 'ibmmq', + description: 'Explaines the error in more detail', + name: 'ibmmq.errorlog.explanation', + type: 'keyword', + }, + 'ibmmq.errorlog.action': { + category: 'ibmmq', + description: 'Defines what to do when the error occurs', + name: 'ibmmq.errorlog.action', + type: 'keyword', + }, + 'ibmmq.errorlog.code': { + category: 'ibmmq', + description: 'Error code.', + name: 'ibmmq.errorlog.code', + type: 'keyword', + }, + 'iptables.ether_type': { + category: 'iptables', + description: 'Value of the ethernet type field identifying the network layer protocol. ', + name: 'iptables.ether_type', + type: 'long', + }, + 'iptables.flow_label': { + category: 'iptables', + description: 'IPv6 flow label. ', + name: 'iptables.flow_label', + type: 'integer', + }, + 'iptables.fragment_flags': { + category: 'iptables', + description: 'IP fragment flags. A combination of CE, DF and MF. ', + name: 'iptables.fragment_flags', + type: 'keyword', + }, + 'iptables.fragment_offset': { + category: 'iptables', + description: 'Offset of the current IP fragment. ', + name: 'iptables.fragment_offset', + type: 'long', + }, + 'iptables.icmp.code': { + category: 'iptables', + description: 'ICMP code. ', + name: 'iptables.icmp.code', + type: 'long', + }, + 'iptables.icmp.id': { + category: 'iptables', + description: 'ICMP ID. ', + name: 'iptables.icmp.id', + type: 'long', + }, + 'iptables.icmp.parameter': { + category: 'iptables', + description: 'ICMP parameter. ', + name: 'iptables.icmp.parameter', + type: 'long', + }, + 'iptables.icmp.redirect': { + category: 'iptables', + description: 'ICMP redirect address. ', + name: 'iptables.icmp.redirect', + type: 'ip', + }, + 'iptables.icmp.seq': { + category: 'iptables', + description: 'ICMP sequence number. ', + name: 'iptables.icmp.seq', + type: 'long', + }, + 'iptables.icmp.type': { + category: 'iptables', + description: 'ICMP type. ', + name: 'iptables.icmp.type', + type: 'long', + }, + 'iptables.id': { + category: 'iptables', + description: 'Packet identifier. ', + name: 'iptables.id', + type: 'long', + }, + 'iptables.incomplete_bytes': { + category: 'iptables', + description: 'Number of incomplete bytes. ', + name: 'iptables.incomplete_bytes', + type: 'long', + }, + 'iptables.input_device': { + category: 'iptables', + description: 'Device that received the packet. ', + name: 'iptables.input_device', + type: 'keyword', + }, + 'iptables.precedence_bits': { + category: 'iptables', + description: 'IP precedence bits. ', + name: 'iptables.precedence_bits', + type: 'short', + }, + 'iptables.tos': { + category: 'iptables', + description: 'IP Type of Service field. ', + name: 'iptables.tos', + type: 'long', + }, + 'iptables.length': { + category: 'iptables', + description: 'Packet length. ', + name: 'iptables.length', + type: 'long', + }, + 'iptables.output_device': { + category: 'iptables', + description: 'Device that output the packet. ', + name: 'iptables.output_device', + type: 'keyword', + }, + 'iptables.tcp.flags': { + category: 'iptables', + description: 'TCP flags. ', + name: 'iptables.tcp.flags', + type: 'keyword', + }, + 'iptables.tcp.reserved_bits': { + category: 'iptables', + description: 'TCP reserved bits. ', + name: 'iptables.tcp.reserved_bits', + type: 'short', + }, + 'iptables.tcp.seq': { + category: 'iptables', + description: 'TCP sequence number. ', + name: 'iptables.tcp.seq', + type: 'long', + }, + 'iptables.tcp.ack': { + category: 'iptables', + description: 'TCP Acknowledgment number. ', + name: 'iptables.tcp.ack', + type: 'long', + }, + 'iptables.tcp.window': { + category: 'iptables', + description: 'Advertised TCP window size. ', + name: 'iptables.tcp.window', + type: 'long', + }, + 'iptables.ttl': { + category: 'iptables', + description: 'Time To Live field. ', + name: 'iptables.ttl', + type: 'integer', + }, + 'iptables.udp.length': { + category: 'iptables', + description: 'Length of the UDP header and payload. ', + name: 'iptables.udp.length', + type: 'long', + }, + 'iptables.ubiquiti.input_zone': { + category: 'iptables', + description: 'Input zone. ', + name: 'iptables.ubiquiti.input_zone', + type: 'keyword', + }, + 'iptables.ubiquiti.output_zone': { + category: 'iptables', + description: 'Output zone. ', + name: 'iptables.ubiquiti.output_zone', + type: 'keyword', + }, + 'iptables.ubiquiti.rule_number': { + category: 'iptables', + description: 'The rule number within the rule set.', + name: 'iptables.ubiquiti.rule_number', + type: 'keyword', + }, + 'iptables.ubiquiti.rule_set': { + category: 'iptables', + description: 'The rule set name.', + name: 'iptables.ubiquiti.rule_set', + type: 'keyword', + }, + 'microsoft.defender_atp.lastUpdateTime': { + category: 'microsoft', + description: 'The date and time (in UTC) the alert was last updated. ', + name: 'microsoft.defender_atp.lastUpdateTime', + type: 'date', + }, + 'microsoft.defender_atp.resolvedTime': { + category: 'microsoft', + description: "The date and time in which the status of the alert was changed to 'Resolved'. ", + name: 'microsoft.defender_atp.resolvedTime', + type: 'date', + }, + 'microsoft.defender_atp.incidentId': { + category: 'microsoft', + description: 'The Incident ID of the Alert. ', + name: 'microsoft.defender_atp.incidentId', + type: 'keyword', + }, + 'microsoft.defender_atp.investigationId': { + category: 'microsoft', + description: 'The Investigation ID related to the Alert. ', + name: 'microsoft.defender_atp.investigationId', + type: 'keyword', + }, + 'microsoft.defender_atp.investigationState': { + category: 'microsoft', + description: 'The current state of the Investigation. ', + name: 'microsoft.defender_atp.investigationState', + type: 'keyword', + }, + 'microsoft.defender_atp.assignedTo': { + category: 'microsoft', + description: 'Owner of the alert. ', + name: 'microsoft.defender_atp.assignedTo', + type: 'keyword', + }, + 'microsoft.defender_atp.status': { + category: 'microsoft', + description: + "Specifies the current status of the alert. Possible values are: 'Unknown', 'New', 'InProgress' and 'Resolved'. ", + name: 'microsoft.defender_atp.status', + type: 'keyword', + }, + 'microsoft.defender_atp.classification': { + category: 'microsoft', + description: + "Specification of the alert. Possible values are: 'Unknown', 'FalsePositive', 'TruePositive'. ", + name: 'microsoft.defender_atp.classification', + type: 'keyword', + }, + 'microsoft.defender_atp.determination': { + category: 'microsoft', + description: + "Specifies the determination of the alert. Possible values are: 'NotAvailable', 'Apt', 'Malware', 'SecurityPersonnel', 'SecurityTesting', 'UnwantedSoftware', 'Other'. ", + name: 'microsoft.defender_atp.determination', + type: 'keyword', + }, + 'microsoft.defender_atp.threatFamilyName': { + category: 'microsoft', + description: 'Threat family. ', + name: 'microsoft.defender_atp.threatFamilyName', + type: 'keyword', + }, + 'microsoft.defender_atp.rbacGroupName': { + category: 'microsoft', + description: 'User group related to the alert ', + name: 'microsoft.defender_atp.rbacGroupName', + type: 'keyword', + }, + 'microsoft.defender_atp.evidence.domainName': { + category: 'microsoft', + description: 'Domain name related to the alert ', + name: 'microsoft.defender_atp.evidence.domainName', + type: 'keyword', + }, + 'microsoft.defender_atp.evidence.ipAddress': { + category: 'microsoft', + description: 'IP address involved in the alert ', + name: 'microsoft.defender_atp.evidence.ipAddress', + type: 'ip', + }, + 'microsoft.defender_atp.evidence.aadUserId': { + category: 'microsoft', + description: 'ID of the user involved in the alert ', + name: 'microsoft.defender_atp.evidence.aadUserId', + type: 'keyword', + }, + 'microsoft.defender_atp.evidence.accountName': { + category: 'microsoft', + description: 'Username of the user involved in the alert ', + name: 'microsoft.defender_atp.evidence.accountName', + type: 'keyword', + }, + 'microsoft.defender_atp.evidence.entityType': { + category: 'microsoft', + description: 'The type of evidence ', + name: 'microsoft.defender_atp.evidence.entityType', + type: 'keyword', + }, + 'microsoft.defender_atp.evidence.userPrincipalName': { + category: 'microsoft', + description: 'Principal name of the user involved in the alert ', + name: 'microsoft.defender_atp.evidence.userPrincipalName', + type: 'keyword', + }, + 'misp.attack_pattern.id': { + category: 'misp', + description: 'Identifier of the threat indicator. ', + name: 'misp.attack_pattern.id', + type: 'keyword', + }, + 'misp.attack_pattern.name': { + category: 'misp', + description: 'Name of the attack pattern. ', + name: 'misp.attack_pattern.name', + type: 'keyword', + }, + 'misp.attack_pattern.description': { + category: 'misp', + description: 'Description of the attack pattern. ', + name: 'misp.attack_pattern.description', + type: 'text', + }, + 'misp.attack_pattern.kill_chain_phases': { + category: 'misp', + description: 'The kill chain phase(s) to which this attack pattern corresponds. ', + name: 'misp.attack_pattern.kill_chain_phases', + type: 'keyword', + }, + 'misp.campaign.id': { + category: 'misp', + description: 'Identifier of the campaign. ', + name: 'misp.campaign.id', + type: 'keyword', + }, + 'misp.campaign.name': { + category: 'misp', + description: 'Name of the campaign. ', + name: 'misp.campaign.name', + type: 'keyword', + }, + 'misp.campaign.description': { + category: 'misp', + description: 'Description of the campaign. ', + name: 'misp.campaign.description', + type: 'text', + }, + 'misp.campaign.aliases': { + category: 'misp', + description: 'Alternative names used to identify this campaign. ', + name: 'misp.campaign.aliases', + type: 'text', + }, + 'misp.campaign.first_seen': { + category: 'misp', + description: 'The time that this Campaign was first seen, in RFC3339 format. ', + name: 'misp.campaign.first_seen', + type: 'date', + }, + 'misp.campaign.last_seen': { + category: 'misp', + description: 'The time that this Campaign was last seen, in RFC3339 format. ', + name: 'misp.campaign.last_seen', + type: 'date', + }, + 'misp.campaign.objective': { + category: 'misp', + description: + "This field defines the Campaign's primary goal, objective, desired outcome, or intended effect. ", + name: 'misp.campaign.objective', + type: 'keyword', + }, + 'misp.course_of_action.id': { + category: 'misp', + description: 'Identifier of the Course of Action. ', + name: 'misp.course_of_action.id', + type: 'keyword', + }, + 'misp.course_of_action.name': { + category: 'misp', + description: 'The name used to identify the Course of Action. ', + name: 'misp.course_of_action.name', + type: 'keyword', + }, + 'misp.course_of_action.description': { + category: 'misp', + description: 'Description of the Course of Action. ', + name: 'misp.course_of_action.description', + type: 'text', + }, + 'misp.identity.id': { + category: 'misp', + description: 'Identifier of the Identity. ', + name: 'misp.identity.id', + type: 'keyword', + }, + 'misp.identity.name': { + category: 'misp', + description: 'The name used to identify the Identity. ', + name: 'misp.identity.name', + type: 'keyword', + }, + 'misp.identity.description': { + category: 'misp', + description: 'Description of the Identity. ', + name: 'misp.identity.description', + type: 'text', + }, + 'misp.identity.identity_class': { + category: 'misp', + description: + 'The type of entity that this Identity describes, e.g., an individual or organization. Open Vocab - identity-class-ov ', + name: 'misp.identity.identity_class', + type: 'keyword', + }, + 'misp.identity.labels': { + category: 'misp', + description: 'The list of roles that this Identity performs. ', + example: 'CEO\n', + name: 'misp.identity.labels', + type: 'keyword', + }, + 'misp.identity.sectors': { + category: 'misp', + description: + 'The list of sectors that this Identity belongs to. Open Vocab - industry-sector-ov ', + name: 'misp.identity.sectors', + type: 'keyword', + }, + 'misp.identity.contact_information': { + category: 'misp', + description: 'The contact information (e-mail, phone number, etc.) for this Identity. ', + name: 'misp.identity.contact_information', + type: 'text', + }, + 'misp.intrusion_set.id': { + category: 'misp', + description: 'Identifier of the Intrusion Set. ', + name: 'misp.intrusion_set.id', + type: 'keyword', + }, + 'misp.intrusion_set.name': { + category: 'misp', + description: 'The name used to identify the Intrusion Set. ', + name: 'misp.intrusion_set.name', + type: 'keyword', + }, + 'misp.intrusion_set.description': { + category: 'misp', + description: 'Description of the Intrusion Set. ', + name: 'misp.intrusion_set.description', + type: 'text', + }, + 'misp.intrusion_set.aliases': { + category: 'misp', + description: 'Alternative names used to identify the Intrusion Set. ', + name: 'misp.intrusion_set.aliases', + type: 'text', + }, + 'misp.intrusion_set.first_seen': { + category: 'misp', + description: 'The time that this Intrusion Set was first seen, in RFC3339 format. ', + name: 'misp.intrusion_set.first_seen', + type: 'date', + }, + 'misp.intrusion_set.last_seen': { + category: 'misp', + description: 'The time that this Intrusion Set was last seen, in RFC3339 format. ', + name: 'misp.intrusion_set.last_seen', + type: 'date', + }, + 'misp.intrusion_set.goals': { + category: 'misp', + description: 'The high level goals of this Intrusion Set, namely, what are they trying to do. ', + name: 'misp.intrusion_set.goals', + type: 'text', + }, + 'misp.intrusion_set.resource_level': { + category: 'misp', + description: + 'This defines the organizational level at which this Intrusion Set typically works. Open Vocab - attack-resource-level-ov ', + name: 'misp.intrusion_set.resource_level', + type: 'text', + }, + 'misp.intrusion_set.primary_motivation': { + category: 'misp', + description: + 'The primary reason, motivation, or purpose behind this Intrusion Set. Open Vocab - attack-motivation-ov ', + name: 'misp.intrusion_set.primary_motivation', + type: 'text', + }, + 'misp.intrusion_set.secondary_motivations': { + category: 'misp', + description: + 'The secondary reasons, motivations, or purposes behind this Intrusion Set. Open Vocab - attack-motivation-ov ', + name: 'misp.intrusion_set.secondary_motivations', + type: 'text', + }, + 'misp.malware.id': { + category: 'misp', + description: 'Identifier of the Malware. ', + name: 'misp.malware.id', + type: 'keyword', + }, + 'misp.malware.name': { + category: 'misp', + description: 'The name used to identify the Malware. ', + name: 'misp.malware.name', + type: 'keyword', + }, + 'misp.malware.description': { + category: 'misp', + description: 'Description of the Malware. ', + name: 'misp.malware.description', + type: 'text', + }, + 'misp.malware.labels': { + category: 'misp', + description: + 'The type of malware being described. Open Vocab - malware-label-ov. adware,backdoor,bot,ddos,dropper,exploit-kit,keylogger,ransomware, remote-access-trojan,resource-exploitation,rogue-security-software,rootkit, screen-capture,spyware,trojan,virus,worm ', + name: 'misp.malware.labels', + type: 'keyword', + }, + 'misp.malware.kill_chain_phases': { + category: 'misp', + description: 'The list of kill chain phases for which this Malware instance can be used. ', + name: 'misp.malware.kill_chain_phases', + type: 'keyword', + format: 'string', + }, + 'misp.note.id': { + category: 'misp', + description: 'Identifier of the Note. ', + name: 'misp.note.id', + type: 'keyword', + }, + 'misp.note.summary': { + category: 'misp', + description: 'A brief description used as a summary of the Note. ', + name: 'misp.note.summary', + type: 'keyword', + }, + 'misp.note.description': { + category: 'misp', + description: 'The content of the Note. ', + name: 'misp.note.description', + type: 'text', + }, + 'misp.note.authors': { + category: 'misp', + description: 'The name of the author(s) of this Note. ', + name: 'misp.note.authors', + type: 'keyword', + }, + 'misp.note.object_refs': { + category: 'misp', + description: 'The STIX Objects (SDOs and SROs) that the note is being applied to. ', + name: 'misp.note.object_refs', + type: 'keyword', + }, + 'misp.threat_indicator.labels': { + category: 'misp', + description: 'list of type open-vocab that specifies the type of indicator. ', + example: 'Domain Watchlist\n', + name: 'misp.threat_indicator.labels', + type: 'keyword', + }, + 'misp.threat_indicator.id': { + category: 'misp', + description: 'Identifier of the threat indicator. ', + name: 'misp.threat_indicator.id', + type: 'keyword', + }, + 'misp.threat_indicator.version': { + category: 'misp', + description: 'Version of the threat indicator. ', + name: 'misp.threat_indicator.version', + type: 'keyword', + }, + 'misp.threat_indicator.type': { + category: 'misp', + description: 'Type of the threat indicator. ', + name: 'misp.threat_indicator.type', + type: 'keyword', + }, + 'misp.threat_indicator.description': { + category: 'misp', + description: 'Description of the threat indicator. ', + name: 'misp.threat_indicator.description', + type: 'text', + }, + 'misp.threat_indicator.feed': { + category: 'misp', + description: 'Name of the threat feed. ', + name: 'misp.threat_indicator.feed', + type: 'text', + }, + 'misp.threat_indicator.valid_from': { + category: 'misp', + description: + 'The time from which this Indicator should be considered valuable intelligence, in RFC3339 format. ', + name: 'misp.threat_indicator.valid_from', + type: 'date', + }, + 'misp.threat_indicator.valid_until': { + category: 'misp', + description: + 'The time at which this Indicator should no longer be considered valuable intelligence. If the valid_until property is omitted, then there is no constraint on the latest time for which the indicator should be used, in RFC3339 format. ', + name: 'misp.threat_indicator.valid_until', + type: 'date', + }, + 'misp.threat_indicator.severity': { + category: 'misp', + description: 'Threat severity to which this indicator corresponds. ', + example: 'high', + name: 'misp.threat_indicator.severity', + type: 'keyword', + format: 'string', + }, + 'misp.threat_indicator.confidence': { + category: 'misp', + description: 'Confidence level to which this indicator corresponds. ', + example: 'high', + name: 'misp.threat_indicator.confidence', + type: 'keyword', + }, + 'misp.threat_indicator.kill_chain_phases': { + category: 'misp', + description: 'The kill chain phase(s) to which this indicator corresponds. ', + name: 'misp.threat_indicator.kill_chain_phases', + type: 'keyword', + format: 'string', + }, + 'misp.threat_indicator.mitre_tactic': { + category: 'misp', + description: 'MITRE tactics to which this indicator corresponds. ', + example: 'Initial Access', + name: 'misp.threat_indicator.mitre_tactic', + type: 'keyword', + format: 'string', + }, + 'misp.threat_indicator.mitre_technique': { + category: 'misp', + description: 'MITRE techniques to which this indicator corresponds. ', + example: 'Drive-by Compromise', + name: 'misp.threat_indicator.mitre_technique', + type: 'keyword', + format: 'string', + }, + 'misp.threat_indicator.attack_pattern': { + category: 'misp', + description: + 'The attack_pattern for this indicator is a STIX Pattern as specified in STIX Version 2.0 Part 5 - STIX Patterning. ', + example: "[destination:ip = '91.219.29.188/32']\n", + name: 'misp.threat_indicator.attack_pattern', + type: 'keyword', + }, + 'misp.threat_indicator.attack_pattern_kql': { + category: 'misp', + description: + 'The attack_pattern for this indicator is KQL query that matches the attack_pattern specified in the STIX Pattern format. ', + example: 'destination.ip: "91.219.29.188/32"\n', + name: 'misp.threat_indicator.attack_pattern_kql', + type: 'keyword', + }, + 'misp.threat_indicator.negate': { + category: 'misp', + description: 'When set to true, it specifies the absence of the attack_pattern. ', + name: 'misp.threat_indicator.negate', + type: 'boolean', + }, + 'misp.threat_indicator.intrusion_set': { + category: 'misp', + description: 'Name of the intrusion set if known. ', + name: 'misp.threat_indicator.intrusion_set', + type: 'keyword', + }, + 'misp.threat_indicator.campaign': { + category: 'misp', + description: 'Name of the attack campaign if known. ', + name: 'misp.threat_indicator.campaign', + type: 'keyword', + }, + 'misp.threat_indicator.threat_actor': { + category: 'misp', + description: 'Name of the threat actor if known. ', + name: 'misp.threat_indicator.threat_actor', + type: 'keyword', + }, + 'misp.observed_data.id': { + category: 'misp', + description: 'Identifier of the Observed Data. ', + name: 'misp.observed_data.id', + type: 'keyword', + }, + 'misp.observed_data.first_observed': { + category: 'misp', + description: 'The beginning of the time window that the data was observed, in RFC3339 format. ', + name: 'misp.observed_data.first_observed', + type: 'date', + }, + 'misp.observed_data.last_observed': { + category: 'misp', + description: 'The end of the time window that the data was observed, in RFC3339 format. ', + name: 'misp.observed_data.last_observed', + type: 'date', + }, + 'misp.observed_data.number_observed': { + category: 'misp', + description: + 'The number of times the data represented in the objects property was observed. This MUST be an integer between 1 and 999,999,999 inclusive. ', + name: 'misp.observed_data.number_observed', + type: 'integer', + }, + 'misp.observed_data.objects': { + category: 'misp', + description: + 'A dictionary of Cyber Observable Objects that describes the single fact that was observed. ', + name: 'misp.observed_data.objects', + type: 'keyword', + }, + 'misp.report.id': { + category: 'misp', + description: 'Identifier of the Report. ', + name: 'misp.report.id', + type: 'keyword', + }, + 'misp.report.labels': { + category: 'misp', + description: + 'This field is an Open Vocabulary that specifies the primary subject of this report. Open Vocab - report-label-ov. threat-report,attack-pattern,campaign,identity,indicator,malware,observed-data,threat-actor,tool,vulnerability ', + name: 'misp.report.labels', + type: 'keyword', + }, + 'misp.report.name': { + category: 'misp', + description: 'The name used to identify the Report. ', + name: 'misp.report.name', + type: 'keyword', + }, + 'misp.report.description': { + category: 'misp', + description: 'A description that provides more details and context about Report. ', + name: 'misp.report.description', + type: 'text', + }, + 'misp.report.published': { + category: 'misp', + description: + 'The date that this report object was officially published by the creator of this report, in RFC3339 format. ', + name: 'misp.report.published', + type: 'date', + }, + 'misp.report.object_refs': { + category: 'misp', + description: 'Specifies the STIX Objects that are referred to by this Report. ', + name: 'misp.report.object_refs', + type: 'text', + }, + 'misp.threat_actor.id': { + category: 'misp', + description: 'Identifier of the Threat Actor. ', + name: 'misp.threat_actor.id', + type: 'keyword', + }, + 'misp.threat_actor.labels': { + category: 'misp', + description: + 'This field specifies the type of threat actor. Open Vocab - threat-actor-label-ov. activist,competitor,crime-syndicate,criminal,hacker,insider-accidental,insider-disgruntled,nation-state,sensationalist,spy,terrorist ', + name: 'misp.threat_actor.labels', + type: 'keyword', + }, + 'misp.threat_actor.name': { + category: 'misp', + description: 'The name used to identify this Threat Actor or Threat Actor group. ', + name: 'misp.threat_actor.name', + type: 'keyword', + }, + 'misp.threat_actor.description': { + category: 'misp', + description: 'A description that provides more details and context about the Threat Actor. ', + name: 'misp.threat_actor.description', + type: 'text', + }, + 'misp.threat_actor.aliases': { + category: 'misp', + description: 'A list of other names that this Threat Actor is believed to use. ', + name: 'misp.threat_actor.aliases', + type: 'text', + }, + 'misp.threat_actor.roles': { + category: 'misp', + description: + 'This is a list of roles the Threat Actor plays. Open Vocab - threat-actor-role-ov. agent,director,independent,sponsor,infrastructure-operator,infrastructure-architect,malware-author ', + name: 'misp.threat_actor.roles', + type: 'text', + }, + 'misp.threat_actor.goals': { + category: 'misp', + description: 'The high level goals of this Threat Actor, namely, what are they trying to do. ', + name: 'misp.threat_actor.goals', + type: 'text', + }, + 'misp.threat_actor.sophistication': { + category: 'misp', + description: + 'The skill, specific knowledge, special training, or expertise a Threat Actor must have to perform the attack. Open Vocab - threat-actor-sophistication-ov. none,minimal,intermediate,advanced,strategic,expert,innovator ', + name: 'misp.threat_actor.sophistication', + type: 'text', + }, + 'misp.threat_actor.resource_level': { + category: 'misp', + description: + 'This defines the organizational level at which this Threat Actor typically works. Open Vocab - attack-resource-level-ov. individual,club,contest,team,organization,government ', + name: 'misp.threat_actor.resource_level', + type: 'text', + }, + 'misp.threat_actor.primary_motivation': { + category: 'misp', + description: + 'The primary reason, motivation, or purpose behind this Threat Actor. Open Vocab - attack-motivation-ov. accidental,coercion,dominance,ideology,notoriety,organizational-gain,personal-gain,personal-satisfaction,revenge,unpredictable ', + name: 'misp.threat_actor.primary_motivation', + type: 'text', + }, + 'misp.threat_actor.secondary_motivations': { + category: 'misp', + description: + 'The secondary reasons, motivations, or purposes behind this Threat Actor. Open Vocab - attack-motivation-ov. accidental,coercion,dominance,ideology,notoriety,organizational-gain,personal-gain,personal-satisfaction,revenge,unpredictable ', + name: 'misp.threat_actor.secondary_motivations', + type: 'text', + }, + 'misp.threat_actor.personal_motivations': { + category: 'misp', + description: + 'The personal reasons, motivations, or purposes of the Threat Actor regardless of organizational goals. Open Vocab - attack-motivation-ov. accidental,coercion,dominance,ideology,notoriety,organizational-gain,personal-gain,personal-satisfaction,revenge,unpredictable ', + name: 'misp.threat_actor.personal_motivations', + type: 'text', + }, + 'misp.tool.id': { + category: 'misp', + description: 'Identifier of the Tool. ', + name: 'misp.tool.id', + type: 'keyword', + }, + 'misp.tool.labels': { + category: 'misp', + description: + 'The kind(s) of tool(s) being described. Open Vocab - tool-label-ov. denial-of-service,exploitation,information-gathering,network-capture,credential-exploitation,remote-access,vulnerability-scanning ', + name: 'misp.tool.labels', + type: 'keyword', + }, + 'misp.tool.name': { + category: 'misp', + description: 'The name used to identify the Tool. ', + name: 'misp.tool.name', + type: 'keyword', + }, + 'misp.tool.description': { + category: 'misp', + description: 'A description that provides more details and context about the Tool. ', + name: 'misp.tool.description', + type: 'text', + }, + 'misp.tool.tool_version': { + category: 'misp', + description: 'The version identifier associated with the Tool. ', + name: 'misp.tool.tool_version', + type: 'keyword', + }, + 'misp.tool.kill_chain_phases': { + category: 'misp', + description: 'The list of kill chain phases for which this Tool instance can be used. ', + name: 'misp.tool.kill_chain_phases', + type: 'text', + }, + 'misp.vulnerability.id': { + category: 'misp', + description: 'Identifier of the Vulnerability. ', + name: 'misp.vulnerability.id', + type: 'keyword', + }, + 'misp.vulnerability.name': { + category: 'misp', + description: 'The name used to identify the Vulnerability. ', + name: 'misp.vulnerability.name', + type: 'keyword', + }, + 'misp.vulnerability.description': { + category: 'misp', + description: 'A description that provides more details and context about the Vulnerability. ', + name: 'misp.vulnerability.description', + type: 'text', + }, + 'mssql.log.origin': { + category: 'mssql', + description: 'Origin of the message, usually the server but it can also be a recovery process', + name: 'mssql.log.origin', + type: 'keyword', + }, + 'o365.audit.Actor.ID': { + category: 'o365', + name: 'o365.audit.Actor.ID', + type: 'keyword', + }, + 'o365.audit.Actor.Type': { + category: 'o365', + name: 'o365.audit.Actor.Type', + type: 'keyword', + }, + 'o365.audit.ActorContextId': { + category: 'o365', + name: 'o365.audit.ActorContextId', + type: 'keyword', + }, + 'o365.audit.ActorIpAddress': { + category: 'o365', + name: 'o365.audit.ActorIpAddress', + type: 'keyword', + }, + 'o365.audit.ActorUserId': { + category: 'o365', + name: 'o365.audit.ActorUserId', + type: 'keyword', + }, + 'o365.audit.ActorYammerUserId': { + category: 'o365', + name: 'o365.audit.ActorYammerUserId', + type: 'keyword', + }, + 'o365.audit.AlertEntityId': { + category: 'o365', + name: 'o365.audit.AlertEntityId', + type: 'keyword', + }, + 'o365.audit.AlertId': { + category: 'o365', + name: 'o365.audit.AlertId', + type: 'keyword', + }, + 'o365.audit.AlertLinks': { + category: 'o365', + name: 'o365.audit.AlertLinks', + type: 'array', + }, + 'o365.audit.AlertType': { + category: 'o365', + name: 'o365.audit.AlertType', + type: 'keyword', + }, + 'o365.audit.AppId': { + category: 'o365', + name: 'o365.audit.AppId', + type: 'keyword', + }, + 'o365.audit.ApplicationDisplayName': { + category: 'o365', + name: 'o365.audit.ApplicationDisplayName', + type: 'keyword', + }, + 'o365.audit.ApplicationId': { + category: 'o365', + name: 'o365.audit.ApplicationId', + type: 'keyword', + }, + 'o365.audit.AzureActiveDirectoryEventType': { + category: 'o365', + name: 'o365.audit.AzureActiveDirectoryEventType', + type: 'keyword', + }, + 'o365.audit.ExchangeMetaData.*': { + category: 'o365', + name: 'o365.audit.ExchangeMetaData.*', + type: 'object', + }, + 'o365.audit.Category': { + category: 'o365', + name: 'o365.audit.Category', + type: 'keyword', + }, + 'o365.audit.ClientAppId': { + category: 'o365', + name: 'o365.audit.ClientAppId', + type: 'keyword', + }, + 'o365.audit.ClientInfoString': { + category: 'o365', + name: 'o365.audit.ClientInfoString', + type: 'keyword', + }, + 'o365.audit.ClientIP': { + category: 'o365', + name: 'o365.audit.ClientIP', + type: 'keyword', + }, + 'o365.audit.ClientIPAddress': { + category: 'o365', + name: 'o365.audit.ClientIPAddress', + type: 'keyword', + }, + 'o365.audit.Comments': { + category: 'o365', + name: 'o365.audit.Comments', + type: 'text', + }, + 'o365.audit.CorrelationId': { + category: 'o365', + name: 'o365.audit.CorrelationId', + type: 'keyword', + }, + 'o365.audit.CreationTime': { + category: 'o365', + name: 'o365.audit.CreationTime', + type: 'keyword', + }, + 'o365.audit.CustomUniqueId': { + category: 'o365', + name: 'o365.audit.CustomUniqueId', + type: 'keyword', + }, + 'o365.audit.Data': { + category: 'o365', + name: 'o365.audit.Data', + type: 'keyword', + }, + 'o365.audit.DataType': { + category: 'o365', + name: 'o365.audit.DataType', + type: 'keyword', + }, + 'o365.audit.EntityType': { + category: 'o365', + name: 'o365.audit.EntityType', + type: 'keyword', + }, + 'o365.audit.EventData': { + category: 'o365', + name: 'o365.audit.EventData', + type: 'keyword', + }, + 'o365.audit.EventSource': { + category: 'o365', + name: 'o365.audit.EventSource', + type: 'keyword', + }, + 'o365.audit.ExceptionInfo.*': { + category: 'o365', + name: 'o365.audit.ExceptionInfo.*', + type: 'object', + }, + 'o365.audit.ExtendedProperties.*': { + category: 'o365', + name: 'o365.audit.ExtendedProperties.*', + type: 'object', + }, + 'o365.audit.ExternalAccess': { + category: 'o365', + name: 'o365.audit.ExternalAccess', + type: 'keyword', + }, + 'o365.audit.GroupName': { + category: 'o365', + name: 'o365.audit.GroupName', + type: 'keyword', + }, + 'o365.audit.Id': { + category: 'o365', + name: 'o365.audit.Id', + type: 'keyword', + }, + 'o365.audit.ImplicitShare': { + category: 'o365', + name: 'o365.audit.ImplicitShare', + type: 'keyword', + }, + 'o365.audit.IncidentId': { + category: 'o365', + name: 'o365.audit.IncidentId', + type: 'keyword', + }, + 'o365.audit.InternalLogonType': { + category: 'o365', + name: 'o365.audit.InternalLogonType', + type: 'keyword', + }, + 'o365.audit.InterSystemsId': { + category: 'o365', + name: 'o365.audit.InterSystemsId', + type: 'keyword', + }, + 'o365.audit.IntraSystemId': { + category: 'o365', + name: 'o365.audit.IntraSystemId', + type: 'keyword', + }, + 'o365.audit.Item.*': { + category: 'o365', + name: 'o365.audit.Item.*', + type: 'object', + }, + 'o365.audit.Item.*.*': { + category: 'o365', + name: 'o365.audit.Item.*.*', + type: 'object', + }, + 'o365.audit.ItemName': { + category: 'o365', + name: 'o365.audit.ItemName', + type: 'keyword', + }, + 'o365.audit.ItemType': { + category: 'o365', + name: 'o365.audit.ItemType', + type: 'keyword', + }, + 'o365.audit.ListId': { + category: 'o365', + name: 'o365.audit.ListId', + type: 'keyword', + }, + 'o365.audit.ListItemUniqueId': { + category: 'o365', + name: 'o365.audit.ListItemUniqueId', + type: 'keyword', + }, + 'o365.audit.LogonError': { + category: 'o365', + name: 'o365.audit.LogonError', + type: 'keyword', + }, + 'o365.audit.LogonType': { + category: 'o365', + name: 'o365.audit.LogonType', + type: 'keyword', + }, + 'o365.audit.LogonUserSid': { + category: 'o365', + name: 'o365.audit.LogonUserSid', + type: 'keyword', + }, + 'o365.audit.MailboxGuid': { + category: 'o365', + name: 'o365.audit.MailboxGuid', + type: 'keyword', + }, + 'o365.audit.MailboxOwnerMasterAccountSid': { + category: 'o365', + name: 'o365.audit.MailboxOwnerMasterAccountSid', + type: 'keyword', + }, + 'o365.audit.MailboxOwnerSid': { + category: 'o365', + name: 'o365.audit.MailboxOwnerSid', + type: 'keyword', + }, + 'o365.audit.MailboxOwnerUPN': { + category: 'o365', + name: 'o365.audit.MailboxOwnerUPN', + type: 'keyword', + }, + 'o365.audit.Members': { + category: 'o365', + name: 'o365.audit.Members', + type: 'array', + }, + 'o365.audit.Members.*': { + category: 'o365', + name: 'o365.audit.Members.*', + type: 'object', + }, + 'o365.audit.ModifiedProperties.*.*': { + category: 'o365', + name: 'o365.audit.ModifiedProperties.*.*', + type: 'object', + }, + 'o365.audit.Name': { + category: 'o365', + name: 'o365.audit.Name', + type: 'keyword', + }, + 'o365.audit.ObjectId': { + category: 'o365', + name: 'o365.audit.ObjectId', + type: 'keyword', + }, + 'o365.audit.Operation': { + category: 'o365', + name: 'o365.audit.Operation', + type: 'keyword', + }, + 'o365.audit.OrganizationId': { + category: 'o365', + name: 'o365.audit.OrganizationId', + type: 'keyword', + }, + 'o365.audit.OrganizationName': { + category: 'o365', + name: 'o365.audit.OrganizationName', + type: 'keyword', + }, + 'o365.audit.OriginatingServer': { + category: 'o365', + name: 'o365.audit.OriginatingServer', + type: 'keyword', + }, + 'o365.audit.Parameters.*': { + category: 'o365', + name: 'o365.audit.Parameters.*', + type: 'object', + }, + 'o365.audit.PolicyDetails': { + category: 'o365', + name: 'o365.audit.PolicyDetails', + type: 'array', + }, + 'o365.audit.PolicyId': { + category: 'o365', + name: 'o365.audit.PolicyId', + type: 'keyword', + }, + 'o365.audit.RecordType': { + category: 'o365', + name: 'o365.audit.RecordType', + type: 'keyword', + }, + 'o365.audit.ResultStatus': { + category: 'o365', + name: 'o365.audit.ResultStatus', + type: 'keyword', + }, + 'o365.audit.SensitiveInfoDetectionIsIncluded': { + category: 'o365', + name: 'o365.audit.SensitiveInfoDetectionIsIncluded', + type: 'keyword', + }, + 'o365.audit.SharePointMetaData.*': { + category: 'o365', + name: 'o365.audit.SharePointMetaData.*', + type: 'object', + }, + 'o365.audit.SessionId': { + category: 'o365', + name: 'o365.audit.SessionId', + type: 'keyword', + }, + 'o365.audit.Severity': { + category: 'o365', + name: 'o365.audit.Severity', + type: 'keyword', + }, + 'o365.audit.Site': { + category: 'o365', + name: 'o365.audit.Site', + type: 'keyword', + }, + 'o365.audit.SiteUrl': { + category: 'o365', + name: 'o365.audit.SiteUrl', + type: 'keyword', + }, + 'o365.audit.Source': { + category: 'o365', + name: 'o365.audit.Source', + type: 'keyword', + }, + 'o365.audit.SourceFileExtension': { + category: 'o365', + name: 'o365.audit.SourceFileExtension', + type: 'keyword', + }, + 'o365.audit.SourceFileName': { + category: 'o365', + name: 'o365.audit.SourceFileName', + type: 'keyword', + }, + 'o365.audit.SourceRelativeUrl': { + category: 'o365', + name: 'o365.audit.SourceRelativeUrl', + type: 'keyword', + }, + 'o365.audit.Status': { + category: 'o365', + name: 'o365.audit.Status', + type: 'keyword', + }, + 'o365.audit.SupportTicketId': { + category: 'o365', + name: 'o365.audit.SupportTicketId', + type: 'keyword', + }, + 'o365.audit.Target.ID': { + category: 'o365', + name: 'o365.audit.Target.ID', + type: 'keyword', + }, + 'o365.audit.Target.Type': { + category: 'o365', + name: 'o365.audit.Target.Type', + type: 'keyword', + }, + 'o365.audit.TargetContextId': { + category: 'o365', + name: 'o365.audit.TargetContextId', + type: 'keyword', + }, + 'o365.audit.TargetUserOrGroupName': { + category: 'o365', + name: 'o365.audit.TargetUserOrGroupName', + type: 'keyword', + }, + 'o365.audit.TargetUserOrGroupType': { + category: 'o365', + name: 'o365.audit.TargetUserOrGroupType', + type: 'keyword', + }, + 'o365.audit.TeamName': { + category: 'o365', + name: 'o365.audit.TeamName', + type: 'keyword', + }, + 'o365.audit.TeamGuid': { + category: 'o365', + name: 'o365.audit.TeamGuid', + type: 'keyword', + }, + 'o365.audit.UniqueSharingId': { + category: 'o365', + name: 'o365.audit.UniqueSharingId', + type: 'keyword', + }, + 'o365.audit.UserAgent': { + category: 'o365', + name: 'o365.audit.UserAgent', + type: 'keyword', + }, + 'o365.audit.UserId': { + category: 'o365', + name: 'o365.audit.UserId', + type: 'keyword', + }, + 'o365.audit.UserKey': { + category: 'o365', + name: 'o365.audit.UserKey', + type: 'keyword', + }, + 'o365.audit.UserType': { + category: 'o365', + name: 'o365.audit.UserType', + type: 'keyword', + }, + 'o365.audit.Version': { + category: 'o365', + name: 'o365.audit.Version', + type: 'keyword', + }, + 'o365.audit.WebId': { + category: 'o365', + name: 'o365.audit.WebId', + type: 'keyword', + }, + 'o365.audit.Workload': { + category: 'o365', + name: 'o365.audit.Workload', + type: 'keyword', + }, + 'o365.audit.YammerNetworkId': { + category: 'o365', + name: 'o365.audit.YammerNetworkId', + type: 'keyword', + }, + 'okta.uuid': { + category: 'okta', + description: 'The unique identifier of the Okta LogEvent. ', + name: 'okta.uuid', + type: 'keyword', + }, + 'okta.event_type': { + category: 'okta', + description: 'The type of the LogEvent. ', + name: 'okta.event_type', + type: 'keyword', + }, + 'okta.version': { + category: 'okta', + description: 'The version of the LogEvent. ', + name: 'okta.version', + type: 'keyword', + }, + 'okta.severity': { + category: 'okta', + description: 'The severity of the LogEvent. Must be one of DEBUG, INFO, WARN, or ERROR. ', + name: 'okta.severity', + type: 'keyword', + }, + 'okta.display_message': { + category: 'okta', + description: 'The display message of the LogEvent. ', + name: 'okta.display_message', + type: 'keyword', + }, + 'okta.actor.id': { + category: 'okta', + description: 'Identifier of the actor. ', + name: 'okta.actor.id', + type: 'keyword', + }, + 'okta.actor.type': { + category: 'okta', + description: 'Type of the actor. ', + name: 'okta.actor.type', + type: 'keyword', + }, + 'okta.actor.alternate_id': { + category: 'okta', + description: 'Alternate identifier of the actor. ', + name: 'okta.actor.alternate_id', + type: 'keyword', + }, + 'okta.actor.display_name': { + category: 'okta', + description: 'Display name of the actor. ', + name: 'okta.actor.display_name', + type: 'keyword', + }, + 'okta.client.ip': { + category: 'okta', + description: 'The IP address of the client. ', + name: 'okta.client.ip', + type: 'ip', + }, + 'okta.client.user_agent.raw_user_agent': { + category: 'okta', + description: 'The raw informaton of the user agent. ', + name: 'okta.client.user_agent.raw_user_agent', + type: 'keyword', + }, + 'okta.client.user_agent.os': { + category: 'okta', + description: 'The OS informaton. ', + name: 'okta.client.user_agent.os', + type: 'keyword', + }, + 'okta.client.user_agent.browser': { + category: 'okta', + description: 'The browser informaton of the client. ', + name: 'okta.client.user_agent.browser', + type: 'keyword', + }, + 'okta.client.zone': { + category: 'okta', + description: 'The zone information of the client. ', + name: 'okta.client.zone', + type: 'keyword', + }, + 'okta.client.device': { + category: 'okta', + description: 'The information of the client device. ', + name: 'okta.client.device', + type: 'keyword', + }, + 'okta.client.id': { + category: 'okta', + description: 'The identifier of the client. ', + name: 'okta.client.id', + type: 'keyword', + }, + 'okta.outcome.reason': { + category: 'okta', + description: 'The reason of the outcome. ', + name: 'okta.outcome.reason', + type: 'keyword', + }, + 'okta.outcome.result': { + category: 'okta', + description: + 'The result of the outcome. Must be one of: SUCCESS, FAILURE, SKIPPED, ALLOW, DENY, CHALLENGE, UNKNOWN. ', + name: 'okta.outcome.result', + type: 'keyword', + }, + 'okta.target.id': { + category: 'okta', + description: 'Identifier of the actor. ', + name: 'okta.target.id', + type: 'keyword', + }, + 'okta.target.type': { + category: 'okta', + description: 'Type of the actor. ', + name: 'okta.target.type', + type: 'keyword', + }, + 'okta.target.alternate_id': { + category: 'okta', + description: 'Alternate identifier of the actor. ', + name: 'okta.target.alternate_id', + type: 'keyword', + }, + 'okta.target.display_name': { + category: 'okta', + description: 'Display name of the actor. ', + name: 'okta.target.display_name', + type: 'keyword', + }, + 'okta.transaction.id': { + category: 'okta', + description: 'Identifier of the transaction. ', + name: 'okta.transaction.id', + type: 'keyword', + }, + 'okta.transaction.type': { + category: 'okta', + description: 'The type of transaction. Must be one of "WEB", "JOB". ', + name: 'okta.transaction.type', + type: 'keyword', + }, + 'okta.debug_context.debug_data.device_fingerprint': { + category: 'okta', + description: 'The fingerprint of the device. ', + name: 'okta.debug_context.debug_data.device_fingerprint', + type: 'keyword', + }, + 'okta.debug_context.debug_data.request_id': { + category: 'okta', + description: 'The identifier of the request. ', + name: 'okta.debug_context.debug_data.request_id', + type: 'keyword', + }, + 'okta.debug_context.debug_data.request_uri': { + category: 'okta', + description: 'The request URI. ', + name: 'okta.debug_context.debug_data.request_uri', + type: 'keyword', + }, + 'okta.debug_context.debug_data.threat_suspected': { + category: 'okta', + description: 'Threat suspected. ', + name: 'okta.debug_context.debug_data.threat_suspected', + type: 'keyword', + }, + 'okta.debug_context.debug_data.url': { + category: 'okta', + description: 'The URL. ', + name: 'okta.debug_context.debug_data.url', + type: 'keyword', + }, + 'okta.authentication_context.authentication_provider': { + category: 'okta', + description: + 'The information about the authentication provider. Must be one of OKTA_AUTHENTICATION_PROVIDER, ACTIVE_DIRECTORY, LDAP, FEDERATION, SOCIAL, FACTOR_PROVIDER. ', + name: 'okta.authentication_context.authentication_provider', + type: 'keyword', + }, + 'okta.authentication_context.authentication_step': { + category: 'okta', + description: 'The authentication step. ', + name: 'okta.authentication_context.authentication_step', + type: 'integer', + }, + 'okta.authentication_context.credential_provider': { + category: 'okta', + description: + 'The information about credential provider. Must be one of OKTA_CREDENTIAL_PROVIDER, RSA, SYMANTEC, GOOGLE, DUO, YUBIKEY. ', + name: 'okta.authentication_context.credential_provider', + type: 'keyword', + }, + 'okta.authentication_context.credential_type': { + category: 'okta', + description: + 'The information about credential type. Must be one of OTP, SMS, PASSWORD, ASSERTION, IWA, EMAIL, OAUTH2, JWT, CERTIFICATE, PRE_SHARED_SYMMETRIC_KEY, OKTA_CLIENT_SESSION, DEVICE_UDID. ', + name: 'okta.authentication_context.credential_type', + type: 'keyword', + }, + 'okta.authentication_context.issuer.id': { + category: 'okta', + description: 'The identifier of the issuer. ', + name: 'okta.authentication_context.issuer.id', + type: 'keyword', + }, + 'okta.authentication_context.issuer.type': { + category: 'okta', + description: 'The type of the issuer. ', + name: 'okta.authentication_context.issuer.type', + type: 'keyword', + }, + 'okta.authentication_context.external_session_id': { + category: 'okta', + description: 'The session identifer of the external session if any. ', + name: 'okta.authentication_context.external_session_id', + type: 'keyword', + }, + 'okta.authentication_context.interface': { + category: 'okta', + description: 'The interface used. e.g., Outlook, Office365, wsTrust ', + name: 'okta.authentication_context.interface', + type: 'keyword', + }, + 'okta.security_context.as.number': { + category: 'okta', + description: 'The AS number. ', + name: 'okta.security_context.as.number', + type: 'integer', + }, + 'okta.security_context.as.organization.name': { + category: 'okta', + description: 'The organization name. ', + name: 'okta.security_context.as.organization.name', + type: 'keyword', + }, + 'okta.security_context.isp': { + category: 'okta', + description: 'The Internet Service Provider. ', + name: 'okta.security_context.isp', + type: 'keyword', + }, + 'okta.security_context.domain': { + category: 'okta', + description: 'The domain name. ', + name: 'okta.security_context.domain', + type: 'keyword', + }, + 'okta.security_context.is_proxy': { + category: 'okta', + description: 'Whether it is a proxy or not. ', + name: 'okta.security_context.is_proxy', + type: 'boolean', + }, + 'okta.request.ip_chain.ip': { + category: 'okta', + description: 'IP address. ', + name: 'okta.request.ip_chain.ip', + type: 'ip', + }, + 'okta.request.ip_chain.version': { + category: 'okta', + description: 'IP version. Must be one of V4, V6. ', + name: 'okta.request.ip_chain.version', + type: 'keyword', + }, + 'okta.request.ip_chain.source': { + category: 'okta', + description: 'Source information. ', + name: 'okta.request.ip_chain.source', + type: 'keyword', + }, + 'okta.request.ip_chain.geographical_context.city': { + category: 'okta', + description: 'The city.', + name: 'okta.request.ip_chain.geographical_context.city', + type: 'keyword', + }, + 'okta.request.ip_chain.geographical_context.state': { + category: 'okta', + description: 'The state.', + name: 'okta.request.ip_chain.geographical_context.state', + type: 'keyword', + }, + 'okta.request.ip_chain.geographical_context.postal_code': { + category: 'okta', + description: 'The postal code.', + name: 'okta.request.ip_chain.geographical_context.postal_code', + type: 'keyword', + }, + 'okta.request.ip_chain.geographical_context.country': { + category: 'okta', + description: 'The country.', + name: 'okta.request.ip_chain.geographical_context.country', + type: 'keyword', + }, + 'okta.request.ip_chain.geographical_context.geolocation': { + category: 'okta', + description: 'Geolocation information. ', + name: 'okta.request.ip_chain.geographical_context.geolocation', + type: 'geo_point', + }, + 'panw.panos.ruleset': { + category: 'panw', + description: 'Name of the rule that matched this session. ', + name: 'panw.panos.ruleset', + type: 'keyword', + }, + 'panw.panos.source.zone': { + category: 'panw', + description: 'Source zone for this session. ', + name: 'panw.panos.source.zone', + type: 'keyword', + }, + 'panw.panos.source.interface': { + category: 'panw', + description: 'Source interface for this session. ', + name: 'panw.panos.source.interface', + type: 'keyword', + }, + 'panw.panos.source.nat.ip': { + category: 'panw', + description: 'Post-NAT source IP. ', + name: 'panw.panos.source.nat.ip', + type: 'ip', + }, + 'panw.panos.source.nat.port': { + category: 'panw', + description: 'Post-NAT source port. ', + name: 'panw.panos.source.nat.port', + type: 'long', + }, + 'panw.panos.destination.zone': { + category: 'panw', + description: 'Destination zone for this session. ', + name: 'panw.panos.destination.zone', + type: 'keyword', + }, + 'panw.panos.destination.interface': { + category: 'panw', + description: 'Destination interface for this session. ', + name: 'panw.panos.destination.interface', + type: 'keyword', + }, + 'panw.panos.destination.nat.ip': { + category: 'panw', + description: 'Post-NAT destination IP. ', + name: 'panw.panos.destination.nat.ip', + type: 'ip', + }, + 'panw.panos.destination.nat.port': { + category: 'panw', + description: 'Post-NAT destination port. ', + name: 'panw.panos.destination.nat.port', + type: 'long', + }, + 'panw.panos.network.pcap_id': { + category: 'panw', + description: 'Packet capture ID for a threat. ', + name: 'panw.panos.network.pcap_id', + type: 'keyword', + }, + 'panw.panos.network.nat.community_id': { + category: 'panw', + description: 'Community ID flow-hash for the NAT 5-tuple. ', + name: 'panw.panos.network.nat.community_id', + type: 'keyword', + }, + 'panw.panos.file.hash': { + category: 'panw', + description: 'Binary hash for a threat file sent to be analyzed by the WildFire service. ', + name: 'panw.panos.file.hash', + type: 'keyword', + }, + 'panw.panos.url.category': { + category: 'panw', + description: + "For threat URLs, it's the URL category. For WildFire, the verdict on the file and is either 'malicious', 'grayware', or 'benign'. ", + name: 'panw.panos.url.category', + type: 'keyword', + }, + 'panw.panos.flow_id': { + category: 'panw', + description: 'Internal numeric identifier for each session. ', + name: 'panw.panos.flow_id', + type: 'keyword', + }, + 'panw.panos.sequence_number': { + category: 'panw', + description: + 'Log entry identifier that is incremented sequentially. Unique for each log type. ', + name: 'panw.panos.sequence_number', + type: 'long', + }, + 'panw.panos.threat.resource': { + category: 'panw', + description: 'URL or file name for a threat. ', + name: 'panw.panos.threat.resource', + type: 'keyword', + }, + 'panw.panos.threat.id': { + category: 'panw', + description: 'Palo Alto Networks identifier for the threat. ', + name: 'panw.panos.threat.id', + type: 'keyword', + }, + 'panw.panos.threat.name': { + category: 'panw', + description: 'Palo Alto Networks name for the threat. ', + name: 'panw.panos.threat.name', + type: 'keyword', + }, + 'panw.panos.action': { + category: 'panw', + description: 'Action taken for the session.', + name: 'panw.panos.action', + type: 'keyword', + }, + 'rabbitmq.log.pid': { + category: 'rabbitmq', + description: 'The Erlang process id', + example: '<0.222.0>', + name: 'rabbitmq.log.pid', + type: 'keyword', + }, + 'sophos.xg.device': { + category: 'sophos', + description: 'device ', + name: 'sophos.xg.device', + type: 'keyword', + }, + 'sophos.xg.date': { + category: 'sophos', + description: 'Date (yyyy-mm-dd) when the event occurred ', + name: 'sophos.xg.date', + type: 'date', + }, + 'sophos.xg.timezone': { + category: 'sophos', + description: 'Time (hh:mm:ss) when the event occurred ', + name: 'sophos.xg.timezone', + type: 'keyword', + }, + 'sophos.xg.device_name': { + category: 'sophos', + description: 'Model number of the device ', + name: 'sophos.xg.device_name', + type: 'keyword', + }, + 'sophos.xg.device_id': { + category: 'sophos', + description: 'Serial number of the device ', + name: 'sophos.xg.device_id', + type: 'keyword', + }, + 'sophos.xg.log_id': { + category: 'sophos', + description: 'Unique 12 characters code (0101011) ', + name: 'sophos.xg.log_id', + type: 'keyword', + }, + 'sophos.xg.log_type': { + category: 'sophos', + description: 'Type of event e.g. firewall event ', + name: 'sophos.xg.log_type', + type: 'keyword', + }, + 'sophos.xg.log_component': { + category: 'sophos', + description: 'Component responsible for logging e.g. Firewall rule ', + name: 'sophos.xg.log_component', + type: 'keyword', + }, + 'sophos.xg.log_subtype': { + category: 'sophos', + description: 'Sub type of event ', + name: 'sophos.xg.log_subtype', + type: 'keyword', + }, + 'sophos.xg.hb_health': { + category: 'sophos', + description: 'Heartbeat status ', + name: 'sophos.xg.hb_health', + type: 'keyword', + }, + 'sophos.xg.priority': { + category: 'sophos', + description: 'Severity level of traffic ', + name: 'sophos.xg.priority', + type: 'keyword', + }, + 'sophos.xg.status': { + category: 'sophos', + description: 'Ultimate status of traffic – Allowed or Denied ', + name: 'sophos.xg.status', + type: 'keyword', + }, + 'sophos.xg.duration': { + category: 'sophos', + description: 'Durability of traffic (seconds) ', + name: 'sophos.xg.duration', + type: 'long', + }, + 'sophos.xg.fw_rule_id': { + category: 'sophos', + description: 'Firewall Rule ID which is applied on the traffic ', + name: 'sophos.xg.fw_rule_id', + type: 'integer', + }, + 'sophos.xg.user_name': { + category: 'sophos', + description: 'user_name ', + name: 'sophos.xg.user_name', + type: 'keyword', + }, + 'sophos.xg.user_group': { + category: 'sophos', + description: 'Group name to which the user belongs ', + name: 'sophos.xg.user_group', + type: 'keyword', + }, + 'sophos.xg.iap': { + category: 'sophos', + description: 'Internet Access policy ID applied on the traffic ', + name: 'sophos.xg.iap', + type: 'keyword', + }, + 'sophos.xg.ips_policy_id': { + category: 'sophos', + description: 'IPS policy ID applied on the traffic ', + name: 'sophos.xg.ips_policy_id', + type: 'integer', + }, + 'sophos.xg.policy_type': { + category: 'sophos', + description: 'Policy type applied to the traffic ', + name: 'sophos.xg.policy_type', + type: 'keyword', + }, + 'sophos.xg.appfilter_policy_id': { + category: 'sophos', + description: 'Application Filter policy applied on the traffic ', + name: 'sophos.xg.appfilter_policy_id', + type: 'integer', + }, + 'sophos.xg.application_filter_policy': { + category: 'sophos', + description: 'Application Filter policy applied on the traffic ', + name: 'sophos.xg.application_filter_policy', + type: 'integer', + }, + 'sophos.xg.application': { + category: 'sophos', + description: 'Application name ', + name: 'sophos.xg.application', + type: 'keyword', + }, + 'sophos.xg.application_name': { + category: 'sophos', + description: 'Application name ', + name: 'sophos.xg.application_name', + type: 'keyword', + }, + 'sophos.xg.application_risk': { + category: 'sophos', + description: 'Risk level assigned to the application ', + name: 'sophos.xg.application_risk', + type: 'keyword', + }, + 'sophos.xg.application_technology': { + category: 'sophos', + description: 'Technology of the application ', + name: 'sophos.xg.application_technology', + type: 'keyword', + }, + 'sophos.xg.application_category': { + category: 'sophos', + description: 'Application is resolved by signature or synchronized application ', + name: 'sophos.xg.application_category', + type: 'keyword', + }, + 'sophos.xg.appresolvedby': { + category: 'sophos', + description: 'Technology of the application ', + name: 'sophos.xg.appresolvedby', + type: 'keyword', + }, + 'sophos.xg.app_is_cloud': { + category: 'sophos', + description: 'Application is Cloud ', + name: 'sophos.xg.app_is_cloud', + type: 'keyword', + }, + 'sophos.xg.in_interface': { + category: 'sophos', + description: 'Interface for incoming traffic, e.g., Port A ', + name: 'sophos.xg.in_interface', + type: 'keyword', + }, + 'sophos.xg.out_interface': { + category: 'sophos', + description: 'Interface for outgoing traffic, e.g., Port B ', + name: 'sophos.xg.out_interface', + type: 'keyword', + }, + 'sophos.xg.src_ip': { + category: 'sophos', + description: 'Original source IP address of traffic ', + name: 'sophos.xg.src_ip', + type: 'ip', + }, + 'sophos.xg.src_mac': { + category: 'sophos', + description: 'Original source MAC address of traffic ', + name: 'sophos.xg.src_mac', + type: 'keyword', + }, + 'sophos.xg.src_country_code': { + category: 'sophos', + description: 'Code of the country to which the source IP belongs ', + name: 'sophos.xg.src_country_code', + type: 'keyword', + }, + 'sophos.xg.dst_ip': { + category: 'sophos', + description: 'Original destination IP address of traffic ', + name: 'sophos.xg.dst_ip', + type: 'ip', + }, + 'sophos.xg.dst_country_code': { + category: 'sophos', + description: 'Code of the country to which the destination IP belongs ', + name: 'sophos.xg.dst_country_code', + type: 'keyword', + }, + 'sophos.xg.protocol': { + category: 'sophos', + description: 'Protocol number of traffic ', + name: 'sophos.xg.protocol', + type: 'keyword', + }, + 'sophos.xg.src_port': { + category: 'sophos', + description: 'Original source port of TCP and UDP traffic ', + name: 'sophos.xg.src_port', + type: 'integer', + }, + 'sophos.xg.dst_port': { + category: 'sophos', + description: 'Original destination port of TCP and UDP traffic ', + name: 'sophos.xg.dst_port', + type: 'integer', + }, + 'sophos.xg.icmp_type': { + category: 'sophos', + description: 'ICMP type of ICMP traffic ', + name: 'sophos.xg.icmp_type', + type: 'keyword', + }, + 'sophos.xg.icmp_code': { + category: 'sophos', + description: 'ICMP code of ICMP traffic ', + name: 'sophos.xg.icmp_code', + type: 'keyword', + }, + 'sophos.xg.sent_pkts': { + category: 'sophos', + description: 'Total number of packets sent ', + name: 'sophos.xg.sent_pkts', + type: 'long', + }, + 'sophos.xg.received_pkts': { + category: 'sophos', + description: 'Total number of packets received ', + name: 'sophos.xg.received_pkts', + type: 'long', + }, + 'sophos.xg.sent_bytes': { + category: 'sophos', + description: 'Total number of bytes sent ', + name: 'sophos.xg.sent_bytes', + type: 'long', + }, + 'sophos.xg.recv_bytes': { + category: 'sophos', + description: 'Total number of bytes received ', + name: 'sophos.xg.recv_bytes', + type: 'long', + }, + 'sophos.xg.trans_src_ ip': { + category: 'sophos', + description: 'Translated source IP address for outgoing traffic ', + name: 'sophos.xg.trans_src_ ip', + type: 'ip', + }, + 'sophos.xg.trans_src_port': { + category: 'sophos', + description: 'Translated source port for outgoing traffic ', + name: 'sophos.xg.trans_src_port', + type: 'integer', + }, + 'sophos.xg.trans_dst_ip': { + category: 'sophos', + description: 'Translated destination IP address for outgoing traffic ', + name: 'sophos.xg.trans_dst_ip', + type: 'ip', + }, + 'sophos.xg.trans_dst_port': { + category: 'sophos', + description: 'Translated destination port for outgoing traffic ', + name: 'sophos.xg.trans_dst_port', + type: 'integer', + }, + 'sophos.xg.srczonetype': { + category: 'sophos', + description: 'Type of source zone, e.g., LAN ', + name: 'sophos.xg.srczonetype', + type: 'keyword', + }, + 'sophos.xg.srczone': { + category: 'sophos', + description: 'Name of source zone ', + name: 'sophos.xg.srczone', + type: 'keyword', + }, + 'sophos.xg.dstzonetype': { + category: 'sophos', + description: 'Type of destination zone, e.g., WAN ', + name: 'sophos.xg.dstzonetype', + type: 'keyword', + }, + 'sophos.xg.dstzone': { + category: 'sophos', + description: 'Name of destination zone ', + name: 'sophos.xg.dstzone', + type: 'keyword', + }, + 'sophos.xg.dir_disp': { + category: 'sophos', + description: 'TPacket direction. Possible values:“org”, “reply”, “” ', + name: 'sophos.xg.dir_disp', + type: 'keyword', + }, + 'sophos.xg.connevent': { + category: 'sophos', + description: 'Event on which this log is generated ', + name: 'sophos.xg.connevent', + type: 'keyword', + }, + 'sophos.xg.conn_id': { + category: 'sophos', + description: 'Unique identifier of connection ', + name: 'sophos.xg.conn_id', + type: 'integer', + }, + 'sophos.xg.vconn_id': { + category: 'sophos', + description: 'Connection ID of the master connection ', + name: 'sophos.xg.vconn_id', + type: 'integer', + }, + 'sophos.xg.idp_policy_id': { + category: 'sophos', + description: 'IPS policy ID which is applied on the traffic ', + name: 'sophos.xg.idp_policy_id', + type: 'integer', + }, + 'sophos.xg.idp_policy_name': { + category: 'sophos', + description: 'IPS policy name i.e. IPS policy name which is applied on the traffic ', + name: 'sophos.xg.idp_policy_name', + type: 'keyword', + }, + 'sophos.xg.signature_id': { + category: 'sophos', + description: 'Signature ID ', + name: 'sophos.xg.signature_id', + type: 'keyword', + }, + 'sophos.xg.signature_msg': { + category: 'sophos', + description: 'Signature messsage ', + name: 'sophos.xg.signature_msg', + type: 'keyword', + }, + 'sophos.xg.classification': { + category: 'sophos', + description: 'Signature classification ', + name: 'sophos.xg.classification', + type: 'keyword', + }, + 'sophos.xg.rule_priority': { + category: 'sophos', + description: 'Priority of IPS policy ', + name: 'sophos.xg.rule_priority', + type: 'keyword', + }, + 'sophos.xg.platform': { + category: 'sophos', + description: 'Platform of the traffic. ', + name: 'sophos.xg.platform', + type: 'keyword', + }, + 'sophos.xg.category': { + category: 'sophos', + description: 'IPS signature category. ', + name: 'sophos.xg.category', + type: 'keyword', + }, + 'sophos.xg.target': { + category: 'sophos', + description: 'Platform of the traffic. ', + name: 'sophos.xg.target', + type: 'keyword', + }, + 'sophos.xg.eventid': { + category: 'sophos', + description: 'ATP Evenet ID ', + name: 'sophos.xg.eventid', + type: 'keyword', + }, + 'sophos.xg.ep_uuid': { + category: 'sophos', + description: 'Endpoint UUID ', + name: 'sophos.xg.ep_uuid', + type: 'keyword', + }, + 'sophos.xg.threatname': { + category: 'sophos', + description: 'ATP threatname ', + name: 'sophos.xg.threatname', + type: 'keyword', + }, + 'sophos.xg.sourceip': { + category: 'sophos', + description: 'Original source IP address of traffic ', + name: 'sophos.xg.sourceip', + type: 'ip', + }, + 'sophos.xg.destinationip': { + category: 'sophos', + description: 'Original destination IP address of traffic ', + name: 'sophos.xg.destinationip', + type: 'ip', + }, + 'sophos.xg.login_user': { + category: 'sophos', + description: 'ATP login user ', + name: 'sophos.xg.login_user', + type: 'keyword', + }, + 'sophos.xg.eventtype': { + category: 'sophos', + description: 'ATP event type ', + name: 'sophos.xg.eventtype', + type: 'keyword', + }, + 'sophos.xg.execution_path': { + category: 'sophos', + description: 'ATP execution path ', + name: 'sophos.xg.execution_path', + type: 'keyword', + }, + 'sophos.xg.av_policy_name': { + category: 'sophos', + description: 'Malware scanning policy name which is applied on the traffic ', + name: 'sophos.xg.av_policy_name', + type: 'keyword', + }, + 'sophos.xg.from_email_address': { + category: 'sophos', + description: 'Sender email address ', + name: 'sophos.xg.from_email_address', + type: 'keyword', + }, + 'sophos.xg.to_email_address': { + category: 'sophos', + description: 'Receipeint email address ', + name: 'sophos.xg.to_email_address', + type: 'keyword', + }, + 'sophos.xg.subject': { + category: 'sophos', + description: 'Email subject ', + name: 'sophos.xg.subject', + type: 'keyword', + }, + 'sophos.xg.mailsize': { + category: 'sophos', + description: 'mailsize ', + name: 'sophos.xg.mailsize', + type: 'integer', + }, + 'sophos.xg.virus': { + category: 'sophos', + description: 'virus name ', + name: 'sophos.xg.virus', + type: 'keyword', + }, + 'sophos.xg.FTP_url': { + category: 'sophos', + description: 'FTP URL from which virus was downloaded ', + name: 'sophos.xg.FTP_url', + type: 'keyword', + }, + 'sophos.xg.FTP_direction': { + category: 'sophos', + description: 'Direction of FTP transfer: Upload or Download ', + name: 'sophos.xg.FTP_direction', + type: 'keyword', + }, + 'sophos.xg.filesize': { + category: 'sophos', + description: 'Size of the file that contained virus ', + name: 'sophos.xg.filesize', + type: 'integer', + }, + 'sophos.xg.filepath': { + category: 'sophos', + description: 'Path of the file containing virus ', + name: 'sophos.xg.filepath', + type: 'keyword', + }, + 'sophos.xg.filename': { + category: 'sophos', + description: 'File name associated with the event ', + name: 'sophos.xg.filename', + type: 'keyword', + }, + 'sophos.xg.ftpcommand': { + category: 'sophos', + description: 'FTP command used when virus was found ', + name: 'sophos.xg.ftpcommand', + type: 'keyword', + }, + 'sophos.xg.url': { + category: 'sophos', + description: 'URL from which virus was downloaded ', + name: 'sophos.xg.url', + type: 'keyword', + }, + 'sophos.xg.domainname': { + category: 'sophos', + description: 'Domain from which virus was downloaded ', + name: 'sophos.xg.domainname', + type: 'keyword', + }, + 'sophos.xg.quarantine': { + category: 'sophos', + description: 'Path and filename of the file quarantined ', + name: 'sophos.xg.quarantine', + type: 'keyword', + }, + 'sophos.xg.src_domainname': { + category: 'sophos', + description: 'Sender domain name ', + name: 'sophos.xg.src_domainname', + type: 'keyword', + }, + 'sophos.xg.dst_domainname': { + category: 'sophos', + description: 'Receiver domain name ', + name: 'sophos.xg.dst_domainname', + type: 'keyword', + }, + 'sophos.xg.reason': { + category: 'sophos', + description: 'Reason why the record was detected as spam/malicious ', + name: 'sophos.xg.reason', + type: 'keyword', + }, + 'sophos.xg.referer': { + category: 'sophos', + description: 'Referer ', + name: 'sophos.xg.referer', + type: 'keyword', + }, + 'sophos.xg.spamaction': { + category: 'sophos', + description: 'Spam Action ', + name: 'sophos.xg.spamaction', + type: 'keyword', + }, + 'sophos.xg.mailid': { + category: 'sophos', + description: 'mailid ', + name: 'sophos.xg.mailid', + type: 'keyword', + }, + 'sophos.xg.quarantine_reason': { + category: 'sophos', + description: 'Quarantine reason ', + name: 'sophos.xg.quarantine_reason', + type: 'keyword', + }, + 'sophos.xg.status_code': { + category: 'sophos', + description: 'Status code ', + name: 'sophos.xg.status_code', + type: 'keyword', + }, + 'sophos.xg.override_token': { + category: 'sophos', + description: 'Override token ', + name: 'sophos.xg.override_token', + type: 'keyword', + }, + 'sophos.xg.con_id': { + category: 'sophos', + description: 'Unique identifier of connection ', + name: 'sophos.xg.con_id', + type: 'integer', + }, + 'sophos.xg.override_authorizer': { + category: 'sophos', + description: 'Override authorizer ', + name: 'sophos.xg.override_authorizer', + type: 'keyword', + }, + 'sophos.xg.transactionid': { + category: 'sophos', + description: 'Transaction ID of the AV scan. ', + name: 'sophos.xg.transactionid', + type: 'keyword', + }, + 'sophos.xg.upload_file_type': { + category: 'sophos', + description: 'Upload file type ', + name: 'sophos.xg.upload_file_type', + type: 'keyword', + }, + 'sophos.xg.upload_file_name': { + category: 'sophos', + description: 'Upload file name ', + name: 'sophos.xg.upload_file_name', + type: 'keyword', + }, + 'sophos.xg.httpresponsecode': { + category: 'sophos', + description: 'code of HTTP response ', + name: 'sophos.xg.httpresponsecode', + type: 'long', + }, + 'sophos.xg.user_gp': { + category: 'sophos', + description: 'Group name to which the user belongs. ', + name: 'sophos.xg.user_gp', + type: 'keyword', + }, + 'sophos.xg.category_type': { + category: 'sophos', + description: 'Type of category under which website falls ', + name: 'sophos.xg.category_type', + type: 'keyword', + }, + 'sophos.xg.download_file_type': { + category: 'sophos', + description: 'Download file type ', + name: 'sophos.xg.download_file_type', + type: 'keyword', + }, + 'sophos.xg.exceptions': { + category: 'sophos', + description: 'List of the checks excluded by web exceptions. ', + name: 'sophos.xg.exceptions', + type: 'keyword', + }, + 'sophos.xg.contenttype': { + category: 'sophos', + description: 'Type of the content ', + name: 'sophos.xg.contenttype', + type: 'keyword', + }, + 'sophos.xg.override_name': { + category: 'sophos', + description: 'Override name ', + name: 'sophos.xg.override_name', + type: 'keyword', + }, + 'sophos.xg.activityname': { + category: 'sophos', + description: 'Web policy activity that matched and caused the policy result. ', + name: 'sophos.xg.activityname', + type: 'keyword', + }, + 'sophos.xg.download_file_name': { + category: 'sophos', + description: 'Download file name ', + name: 'sophos.xg.download_file_name', + type: 'keyword', + }, + 'sophos.xg.sha1sum': { + category: 'sophos', + description: 'SHA1 checksum of the item being analyzed ', + name: 'sophos.xg.sha1sum', + type: 'keyword', + }, + 'sophos.xg.message_id': { + category: 'sophos', + description: 'Message ID ', + name: 'sophos.xg.message_id', + type: 'keyword', + }, + 'sophos.xg.connid': { + category: 'sophos', + description: 'Connection ID ', + name: 'sophos.xg.connid', + type: 'keyword', + }, + 'sophos.xg.message': { + category: 'sophos', + description: 'Message ', + name: 'sophos.xg.message', + type: 'keyword', + }, + 'sophos.xg.email_subject': { + category: 'sophos', + description: 'Email Subject ', + name: 'sophos.xg.email_subject', + type: 'keyword', + }, + 'sophos.xg.file_path': { + category: 'sophos', + description: 'File path ', + name: 'sophos.xg.file_path', + type: 'keyword', + }, + 'sophos.xg.dstdomain': { + category: 'sophos', + description: 'Destination Domain ', + name: 'sophos.xg.dstdomain', + type: 'keyword', + }, + 'sophos.xg.file_size': { + category: 'sophos', + description: 'File Size ', + name: 'sophos.xg.file_size', + type: 'integer', + }, + 'sophos.xg.transaction_id': { + category: 'sophos', + description: 'Transaction ID ', + name: 'sophos.xg.transaction_id', + type: 'keyword', + }, + 'sophos.xg.website': { + category: 'sophos', + description: 'Website ', + name: 'sophos.xg.website', + type: 'keyword', + }, + 'sophos.xg.file_name': { + category: 'sophos', + description: 'Filename ', + name: 'sophos.xg.file_name', + type: 'keyword', + }, + 'sophos.xg.context_prefix': { + category: 'sophos', + description: 'Content Prefix ', + name: 'sophos.xg.context_prefix', + type: 'keyword', + }, + 'sophos.xg.site_category': { + category: 'sophos', + description: 'Site Category ', + name: 'sophos.xg.site_category', + type: 'keyword', + }, + 'sophos.xg.context_suffix': { + category: 'sophos', + description: 'Context Suffix ', + name: 'sophos.xg.context_suffix', + type: 'keyword', + }, + 'sophos.xg.dictionary_name': { + category: 'sophos', + description: 'Dictionary Name ', + name: 'sophos.xg.dictionary_name', + type: 'keyword', + }, + 'sophos.xg.action': { + category: 'sophos', + description: 'Event Action ', + name: 'sophos.xg.action', + type: 'keyword', + }, + 'sophos.xg.user': { + category: 'sophos', + description: 'User ', + name: 'sophos.xg.user', + type: 'keyword', + }, + 'sophos.xg.context_match': { + category: 'sophos', + description: 'Context Match ', + name: 'sophos.xg.context_match', + type: 'keyword', + }, + 'sophos.xg.direction': { + category: 'sophos', + description: 'Direction ', + name: 'sophos.xg.direction', + type: 'keyword', + }, + 'sophos.xg.auth_client': { + category: 'sophos', + description: 'Auth Client ', + name: 'sophos.xg.auth_client', + type: 'keyword', + }, + 'sophos.xg.auth_mechanism': { + category: 'sophos', + description: 'Auth mechanism ', + name: 'sophos.xg.auth_mechanism', + type: 'keyword', + }, + 'sophos.xg.connectionname': { + category: 'sophos', + description: 'Connectionname ', + name: 'sophos.xg.connectionname', + type: 'keyword', + }, + 'sophos.xg.remotenetwork': { + category: 'sophos', + description: 'remotenetwork ', + name: 'sophos.xg.remotenetwork', + type: 'keyword', + }, + 'sophos.xg.localgateway': { + category: 'sophos', + description: 'Localgateway ', + name: 'sophos.xg.localgateway', + type: 'keyword', + }, + 'sophos.xg.localnetwork': { + category: 'sophos', + description: 'Localnetwork ', + name: 'sophos.xg.localnetwork', + type: 'keyword', + }, + 'sophos.xg.connectiontype': { + category: 'sophos', + description: 'Connectiontype ', + name: 'sophos.xg.connectiontype', + type: 'keyword', + }, + 'sophos.xg.oldversion': { + category: 'sophos', + description: 'Oldversion ', + name: 'sophos.xg.oldversion', + type: 'keyword', + }, + 'sophos.xg.newversion': { + category: 'sophos', + description: 'Newversion ', + name: 'sophos.xg.newversion', + type: 'keyword', + }, + 'sophos.xg.ipaddress': { + category: 'sophos', + description: 'Ipaddress ', + name: 'sophos.xg.ipaddress', + type: 'keyword', + }, + 'sophos.xg.client_physical_address': { + category: 'sophos', + description: 'Client physical address ', + name: 'sophos.xg.client_physical_address', + type: 'keyword', + }, + 'sophos.xg.client_host_name': { + category: 'sophos', + description: 'Client host name ', + name: 'sophos.xg.client_host_name', + type: 'keyword', + }, + 'sophos.xg.raw_data': { + category: 'sophos', + description: 'Raw data ', + name: 'sophos.xg.raw_data', + type: 'keyword', + }, + 'sophos.xg.Mode': { + category: 'sophos', + description: 'Mode ', + name: 'sophos.xg.Mode', + type: 'keyword', + }, + 'sophos.xg.sessionid': { + category: 'sophos', + description: 'Sessionid ', + name: 'sophos.xg.sessionid', + type: 'keyword', + }, + 'sophos.xg.starttime': { + category: 'sophos', + description: 'Starttime ', + name: 'sophos.xg.starttime', + type: 'date', + }, + 'sophos.xg.remote_ip': { + category: 'sophos', + description: 'Remote IP ', + name: 'sophos.xg.remote_ip', + type: 'ip', + }, + 'sophos.xg.timestamp': { + category: 'sophos', + description: 'timestamp ', + name: 'sophos.xg.timestamp', + type: 'date', + }, + 'sophos.xg.SysLog_SERVER_NAME': { + category: 'sophos', + description: 'SysLog SERVER NAME ', + name: 'sophos.xg.SysLog_SERVER_NAME', + type: 'keyword', + }, + 'sophos.xg.backup_mode': { + category: 'sophos', + description: 'Backup mode ', + name: 'sophos.xg.backup_mode', + type: 'keyword', + }, + 'sophos.xg.source': { + category: 'sophos', + description: 'Source ', + name: 'sophos.xg.source', + type: 'keyword', + }, + 'sophos.xg.server': { + category: 'sophos', + description: 'Server ', + name: 'sophos.xg.server', + type: 'keyword', + }, + 'sophos.xg.host': { + category: 'sophos', + description: 'Host ', + name: 'sophos.xg.host', + type: 'keyword', + }, + 'sophos.xg.responsetime': { + category: 'sophos', + description: 'Responsetime ', + name: 'sophos.xg.responsetime', + type: 'long', + }, + 'sophos.xg.cookie': { + category: 'sophos', + description: 'cookie ', + name: 'sophos.xg.cookie', + type: 'keyword', + }, + 'sophos.xg.querystring': { + category: 'sophos', + description: 'querystring ', + name: 'sophos.xg.querystring', + type: 'keyword', + }, + 'sophos.xg.extra': { + category: 'sophos', + description: 'extra ', + name: 'sophos.xg.extra', + type: 'keyword', + }, + 'sophos.xg.PHPSESSID': { + category: 'sophos', + description: 'PHPSESSID ', + name: 'sophos.xg.PHPSESSID', + type: 'keyword', + }, + 'sophos.xg.start_time': { + category: 'sophos', + description: 'Start time ', + name: 'sophos.xg.start_time', + type: 'date', + }, + 'sophos.xg.eventtime': { + category: 'sophos', + description: 'Event time ', + name: 'sophos.xg.eventtime', + type: 'date', + }, + 'sophos.xg.red_id': { + category: 'sophos', + description: 'RED ID ', + name: 'sophos.xg.red_id', + type: 'keyword', + }, + 'sophos.xg.branch_name': { + category: 'sophos', + description: 'Branch Name ', + name: 'sophos.xg.branch_name', + type: 'keyword', + }, + 'sophos.xg.updatedip': { + category: 'sophos', + description: 'updatedip ', + name: 'sophos.xg.updatedip', + type: 'ip', + }, + 'sophos.xg.idle_cpu': { + category: 'sophos', + description: 'idle ## ', + name: 'sophos.xg.idle_cpu', + type: 'float', + }, + 'sophos.xg.system_cpu': { + category: 'sophos', + description: 'system ', + name: 'sophos.xg.system_cpu', + type: 'float', + }, + 'sophos.xg.user_cpu': { + category: 'sophos', + description: 'system ', + name: 'sophos.xg.user_cpu', + type: 'float', + }, + 'sophos.xg.used': { + category: 'sophos', + description: 'used ', + name: 'sophos.xg.used', + type: 'integer', + }, + 'sophos.xg.unit': { + category: 'sophos', + description: 'unit ', + name: 'sophos.xg.unit', + type: 'keyword', + }, + 'sophos.xg.total_memory': { + category: 'sophos', + description: 'Total Memory ', + name: 'sophos.xg.total_memory', + type: 'integer', + }, + 'sophos.xg.free': { + category: 'sophos', + description: 'free ', + name: 'sophos.xg.free', + type: 'integer', + }, + 'sophos.xg.transmittederrors': { + category: 'sophos', + description: 'transmitted errors ', + name: 'sophos.xg.transmittederrors', + type: 'keyword', + }, + 'sophos.xg.receivederrors': { + category: 'sophos', + description: 'received errors ', + name: 'sophos.xg.receivederrors', + type: 'keyword', + }, + 'sophos.xg.receivedkbits': { + category: 'sophos', + description: 'received kbits ', + name: 'sophos.xg.receivedkbits', + type: 'long', + }, + 'sophos.xg.transmittedkbits': { + category: 'sophos', + description: 'transmitted kbits ', + name: 'sophos.xg.transmittedkbits', + type: 'long', + }, + 'sophos.xg.transmitteddrops': { + category: 'sophos', + description: 'transmitted drops ', + name: 'sophos.xg.transmitteddrops', + type: 'long', + }, + 'sophos.xg.receiveddrops': { + category: 'sophos', + description: 'received drops ', + name: 'sophos.xg.receiveddrops', + type: 'long', + }, + 'sophos.xg.collisions': { + category: 'sophos', + description: 'collisions ', + name: 'sophos.xg.collisions', + type: 'long', + }, + 'sophos.xg.interface': { + category: 'sophos', + description: 'interface ', + name: 'sophos.xg.interface', + type: 'keyword', + }, + 'sophos.xg.Configuration': { + category: 'sophos', + description: 'Configuration ', + name: 'sophos.xg.Configuration', + type: 'float', + }, + 'sophos.xg.Reports': { + category: 'sophos', + description: 'Reports ', + name: 'sophos.xg.Reports', + type: 'float', + }, + 'sophos.xg.Signature': { + category: 'sophos', + description: 'Signature ', + name: 'sophos.xg.Signature', + type: 'float', + }, + 'sophos.xg.Temp': { + category: 'sophos', + description: 'Temp ', + name: 'sophos.xg.Temp', + type: 'float', + }, + 'sophos.xg.users': { + category: 'sophos', + description: 'users ', + name: 'sophos.xg.users', + type: 'keyword', + }, + 'sophos.xg.ssid': { + category: 'sophos', + description: 'ssid ', + name: 'sophos.xg.ssid', + type: 'keyword', + }, + 'sophos.xg.ap': { + category: 'sophos', + description: 'ap ', + name: 'sophos.xg.ap', + type: 'keyword', + }, + 'sophos.xg.clients_conn_ssid': { + category: 'sophos', + description: 'clients connection ssid ', + name: 'sophos.xg.clients_conn_ssid', + type: 'keyword', + }, + 'suricata.eve.event_type': { + category: 'suricata', + name: 'suricata.eve.event_type', + type: 'keyword', + }, + 'suricata.eve.app_proto_orig': { + category: 'suricata', + name: 'suricata.eve.app_proto_orig', + type: 'keyword', + }, + 'suricata.eve.tcp.tcp_flags': { + category: 'suricata', + name: 'suricata.eve.tcp.tcp_flags', + type: 'keyword', + }, + 'suricata.eve.tcp.psh': { + category: 'suricata', + name: 'suricata.eve.tcp.psh', + type: 'boolean', + }, + 'suricata.eve.tcp.tcp_flags_tc': { + category: 'suricata', + name: 'suricata.eve.tcp.tcp_flags_tc', + type: 'keyword', + }, + 'suricata.eve.tcp.ack': { + category: 'suricata', + name: 'suricata.eve.tcp.ack', + type: 'boolean', + }, + 'suricata.eve.tcp.syn': { + category: 'suricata', + name: 'suricata.eve.tcp.syn', + type: 'boolean', + }, + 'suricata.eve.tcp.state': { + category: 'suricata', + name: 'suricata.eve.tcp.state', + type: 'keyword', + }, + 'suricata.eve.tcp.tcp_flags_ts': { + category: 'suricata', + name: 'suricata.eve.tcp.tcp_flags_ts', + type: 'keyword', + }, + 'suricata.eve.tcp.rst': { + category: 'suricata', + name: 'suricata.eve.tcp.rst', + type: 'boolean', + }, + 'suricata.eve.tcp.fin': { + category: 'suricata', + name: 'suricata.eve.tcp.fin', + type: 'boolean', + }, + 'suricata.eve.fileinfo.sha1': { + category: 'suricata', + name: 'suricata.eve.fileinfo.sha1', + type: 'keyword', + }, + 'suricata.eve.fileinfo.filename': { + category: 'suricata', + name: 'suricata.eve.fileinfo.filename', + type: 'alias', + }, + 'suricata.eve.fileinfo.tx_id': { + category: 'suricata', + name: 'suricata.eve.fileinfo.tx_id', + type: 'long', + }, + 'suricata.eve.fileinfo.state': { + category: 'suricata', + name: 'suricata.eve.fileinfo.state', + type: 'keyword', + }, + 'suricata.eve.fileinfo.stored': { + category: 'suricata', + name: 'suricata.eve.fileinfo.stored', + type: 'boolean', + }, + 'suricata.eve.fileinfo.gaps': { + category: 'suricata', + name: 'suricata.eve.fileinfo.gaps', + type: 'boolean', + }, + 'suricata.eve.fileinfo.sha256': { + category: 'suricata', + name: 'suricata.eve.fileinfo.sha256', + type: 'keyword', + }, + 'suricata.eve.fileinfo.md5': { + category: 'suricata', + name: 'suricata.eve.fileinfo.md5', + type: 'keyword', + }, + 'suricata.eve.fileinfo.size': { + category: 'suricata', + name: 'suricata.eve.fileinfo.size', + type: 'alias', + }, + 'suricata.eve.icmp_type': { + category: 'suricata', + name: 'suricata.eve.icmp_type', + type: 'long', + }, + 'suricata.eve.dest_port': { + category: 'suricata', + name: 'suricata.eve.dest_port', + type: 'alias', + }, + 'suricata.eve.src_port': { + category: 'suricata', + name: 'suricata.eve.src_port', + type: 'alias', + }, + 'suricata.eve.proto': { + category: 'suricata', + name: 'suricata.eve.proto', + type: 'alias', + }, + 'suricata.eve.pcap_cnt': { + category: 'suricata', + name: 'suricata.eve.pcap_cnt', + type: 'long', + }, + 'suricata.eve.src_ip': { + category: 'suricata', + name: 'suricata.eve.src_ip', + type: 'alias', + }, + 'suricata.eve.dns.type': { + category: 'suricata', + name: 'suricata.eve.dns.type', + type: 'keyword', + }, + 'suricata.eve.dns.rrtype': { + category: 'suricata', + name: 'suricata.eve.dns.rrtype', + type: 'keyword', + }, + 'suricata.eve.dns.rrname': { + category: 'suricata', + name: 'suricata.eve.dns.rrname', + type: 'keyword', + }, + 'suricata.eve.dns.rdata': { + category: 'suricata', + name: 'suricata.eve.dns.rdata', + type: 'keyword', + }, + 'suricata.eve.dns.tx_id': { + category: 'suricata', + name: 'suricata.eve.dns.tx_id', + type: 'long', + }, + 'suricata.eve.dns.ttl': { + category: 'suricata', + name: 'suricata.eve.dns.ttl', + type: 'long', + }, + 'suricata.eve.dns.rcode': { + category: 'suricata', + name: 'suricata.eve.dns.rcode', + type: 'keyword', + }, + 'suricata.eve.dns.id': { + category: 'suricata', + name: 'suricata.eve.dns.id', + type: 'long', + }, + 'suricata.eve.flow_id': { + category: 'suricata', + name: 'suricata.eve.flow_id', + type: 'keyword', + }, + 'suricata.eve.email.status': { + category: 'suricata', + name: 'suricata.eve.email.status', + type: 'keyword', + }, + 'suricata.eve.dest_ip': { + category: 'suricata', + name: 'suricata.eve.dest_ip', + type: 'alias', + }, + 'suricata.eve.icmp_code': { + category: 'suricata', + name: 'suricata.eve.icmp_code', + type: 'long', + }, + 'suricata.eve.http.status': { + category: 'suricata', + name: 'suricata.eve.http.status', + type: 'alias', + }, + 'suricata.eve.http.redirect': { + category: 'suricata', + name: 'suricata.eve.http.redirect', + type: 'keyword', + }, + 'suricata.eve.http.http_user_agent': { + category: 'suricata', + name: 'suricata.eve.http.http_user_agent', + type: 'alias', + }, + 'suricata.eve.http.protocol': { + category: 'suricata', + name: 'suricata.eve.http.protocol', + type: 'keyword', + }, + 'suricata.eve.http.http_refer': { + category: 'suricata', + name: 'suricata.eve.http.http_refer', + type: 'alias', + }, + 'suricata.eve.http.url': { + category: 'suricata', + name: 'suricata.eve.http.url', + type: 'alias', + }, + 'suricata.eve.http.hostname': { + category: 'suricata', + name: 'suricata.eve.http.hostname', + type: 'alias', + }, + 'suricata.eve.http.length': { + category: 'suricata', + name: 'suricata.eve.http.length', + type: 'alias', + }, + 'suricata.eve.http.http_method': { + category: 'suricata', + name: 'suricata.eve.http.http_method', + type: 'alias', + }, + 'suricata.eve.http.http_content_type': { + category: 'suricata', + name: 'suricata.eve.http.http_content_type', + type: 'keyword', + }, + 'suricata.eve.timestamp': { + category: 'suricata', + name: 'suricata.eve.timestamp', + type: 'alias', + }, + 'suricata.eve.in_iface': { + category: 'suricata', + name: 'suricata.eve.in_iface', + type: 'keyword', + }, + 'suricata.eve.alert.category': { + category: 'suricata', + name: 'suricata.eve.alert.category', + type: 'keyword', + }, + 'suricata.eve.alert.severity': { + category: 'suricata', + name: 'suricata.eve.alert.severity', + type: 'alias', + }, + 'suricata.eve.alert.rev': { + category: 'suricata', + name: 'suricata.eve.alert.rev', + type: 'long', + }, + 'suricata.eve.alert.gid': { + category: 'suricata', + name: 'suricata.eve.alert.gid', + type: 'long', + }, + 'suricata.eve.alert.signature': { + category: 'suricata', + name: 'suricata.eve.alert.signature', + type: 'keyword', + }, + 'suricata.eve.alert.action': { + category: 'suricata', + name: 'suricata.eve.alert.action', + type: 'alias', + }, + 'suricata.eve.alert.signature_id': { + category: 'suricata', + name: 'suricata.eve.alert.signature_id', + type: 'long', + }, + 'suricata.eve.ssh.client.proto_version': { + category: 'suricata', + name: 'suricata.eve.ssh.client.proto_version', + type: 'keyword', + }, + 'suricata.eve.ssh.client.software_version': { + category: 'suricata', + name: 'suricata.eve.ssh.client.software_version', + type: 'keyword', + }, + 'suricata.eve.ssh.server.proto_version': { + category: 'suricata', + name: 'suricata.eve.ssh.server.proto_version', + type: 'keyword', + }, + 'suricata.eve.ssh.server.software_version': { + category: 'suricata', + name: 'suricata.eve.ssh.server.software_version', + type: 'keyword', + }, + 'suricata.eve.stats.capture.kernel_packets': { + category: 'suricata', + name: 'suricata.eve.stats.capture.kernel_packets', + type: 'long', + }, + 'suricata.eve.stats.capture.kernel_drops': { + category: 'suricata', + name: 'suricata.eve.stats.capture.kernel_drops', + type: 'long', + }, + 'suricata.eve.stats.capture.kernel_ifdrops': { + category: 'suricata', + name: 'suricata.eve.stats.capture.kernel_ifdrops', + type: 'long', + }, + 'suricata.eve.stats.uptime': { + category: 'suricata', + name: 'suricata.eve.stats.uptime', + type: 'long', + }, + 'suricata.eve.stats.detect.alert': { + category: 'suricata', + name: 'suricata.eve.stats.detect.alert', + type: 'long', + }, + 'suricata.eve.stats.http.memcap': { + category: 'suricata', + name: 'suricata.eve.stats.http.memcap', + type: 'long', + }, + 'suricata.eve.stats.http.memuse': { + category: 'suricata', + name: 'suricata.eve.stats.http.memuse', + type: 'long', + }, + 'suricata.eve.stats.file_store.open_files': { + category: 'suricata', + name: 'suricata.eve.stats.file_store.open_files', + type: 'long', + }, + 'suricata.eve.stats.defrag.max_frag_hits': { + category: 'suricata', + name: 'suricata.eve.stats.defrag.max_frag_hits', + type: 'long', + }, + 'suricata.eve.stats.defrag.ipv4.timeouts': { + category: 'suricata', + name: 'suricata.eve.stats.defrag.ipv4.timeouts', + type: 'long', + }, + 'suricata.eve.stats.defrag.ipv4.fragments': { + category: 'suricata', + name: 'suricata.eve.stats.defrag.ipv4.fragments', + type: 'long', + }, + 'suricata.eve.stats.defrag.ipv4.reassembled': { + category: 'suricata', + name: 'suricata.eve.stats.defrag.ipv4.reassembled', + type: 'long', + }, + 'suricata.eve.stats.defrag.ipv6.timeouts': { + category: 'suricata', + name: 'suricata.eve.stats.defrag.ipv6.timeouts', + type: 'long', + }, + 'suricata.eve.stats.defrag.ipv6.fragments': { + category: 'suricata', + name: 'suricata.eve.stats.defrag.ipv6.fragments', + type: 'long', + }, + 'suricata.eve.stats.defrag.ipv6.reassembled': { + category: 'suricata', + name: 'suricata.eve.stats.defrag.ipv6.reassembled', + type: 'long', + }, + 'suricata.eve.stats.flow.tcp_reuse': { + category: 'suricata', + name: 'suricata.eve.stats.flow.tcp_reuse', + type: 'long', + }, + 'suricata.eve.stats.flow.udp': { + category: 'suricata', + name: 'suricata.eve.stats.flow.udp', + type: 'long', + }, + 'suricata.eve.stats.flow.memcap': { + category: 'suricata', + name: 'suricata.eve.stats.flow.memcap', + type: 'long', + }, + 'suricata.eve.stats.flow.emerg_mode_entered': { + category: 'suricata', + name: 'suricata.eve.stats.flow.emerg_mode_entered', + type: 'long', + }, + 'suricata.eve.stats.flow.emerg_mode_over': { + category: 'suricata', + name: 'suricata.eve.stats.flow.emerg_mode_over', + type: 'long', + }, + 'suricata.eve.stats.flow.tcp': { + category: 'suricata', + name: 'suricata.eve.stats.flow.tcp', + type: 'long', + }, + 'suricata.eve.stats.flow.icmpv6': { + category: 'suricata', + name: 'suricata.eve.stats.flow.icmpv6', + type: 'long', + }, + 'suricata.eve.stats.flow.icmpv4': { + category: 'suricata', + name: 'suricata.eve.stats.flow.icmpv4', + type: 'long', + }, + 'suricata.eve.stats.flow.spare': { + category: 'suricata', + name: 'suricata.eve.stats.flow.spare', + type: 'long', + }, + 'suricata.eve.stats.flow.memuse': { + category: 'suricata', + name: 'suricata.eve.stats.flow.memuse', + type: 'long', + }, + 'suricata.eve.stats.tcp.pseudo_failed': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.pseudo_failed', + type: 'long', + }, + 'suricata.eve.stats.tcp.ssn_memcap_drop': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.ssn_memcap_drop', + type: 'long', + }, + 'suricata.eve.stats.tcp.insert_data_overlap_fail': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.insert_data_overlap_fail', + type: 'long', + }, + 'suricata.eve.stats.tcp.sessions': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.sessions', + type: 'long', + }, + 'suricata.eve.stats.tcp.pseudo': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.pseudo', + type: 'long', + }, + 'suricata.eve.stats.tcp.synack': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.synack', + type: 'long', + }, + 'suricata.eve.stats.tcp.insert_data_normal_fail': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.insert_data_normal_fail', + type: 'long', + }, + 'suricata.eve.stats.tcp.syn': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.syn', + type: 'long', + }, + 'suricata.eve.stats.tcp.memuse': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.memuse', + type: 'long', + }, + 'suricata.eve.stats.tcp.invalid_checksum': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.invalid_checksum', + type: 'long', + }, + 'suricata.eve.stats.tcp.segment_memcap_drop': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.segment_memcap_drop', + type: 'long', + }, + 'suricata.eve.stats.tcp.overlap': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.overlap', + type: 'long', + }, + 'suricata.eve.stats.tcp.insert_list_fail': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.insert_list_fail', + type: 'long', + }, + 'suricata.eve.stats.tcp.rst': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.rst', + type: 'long', + }, + 'suricata.eve.stats.tcp.stream_depth_reached': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.stream_depth_reached', + type: 'long', + }, + 'suricata.eve.stats.tcp.reassembly_memuse': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.reassembly_memuse', + type: 'long', + }, + 'suricata.eve.stats.tcp.reassembly_gap': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.reassembly_gap', + type: 'long', + }, + 'suricata.eve.stats.tcp.overlap_diff_data': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.overlap_diff_data', + type: 'long', + }, + 'suricata.eve.stats.tcp.no_flow': { + category: 'suricata', + name: 'suricata.eve.stats.tcp.no_flow', + type: 'long', + }, + 'suricata.eve.stats.decoder.avg_pkt_size': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.avg_pkt_size', + type: 'long', + }, + 'suricata.eve.stats.decoder.bytes': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.bytes', + type: 'long', + }, + 'suricata.eve.stats.decoder.tcp': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.tcp', + type: 'long', + }, + 'suricata.eve.stats.decoder.raw': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.raw', + type: 'long', + }, + 'suricata.eve.stats.decoder.ppp': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.ppp', + type: 'long', + }, + 'suricata.eve.stats.decoder.vlan_qinq': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.vlan_qinq', + type: 'long', + }, + 'suricata.eve.stats.decoder.null': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.null', + type: 'long', + }, + 'suricata.eve.stats.decoder.ltnull.unsupported_type': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.ltnull.unsupported_type', + type: 'long', + }, + 'suricata.eve.stats.decoder.ltnull.pkt_too_small': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.ltnull.pkt_too_small', + type: 'long', + }, + 'suricata.eve.stats.decoder.invalid': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.invalid', + type: 'long', + }, + 'suricata.eve.stats.decoder.gre': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.gre', + type: 'long', + }, + 'suricata.eve.stats.decoder.ipv4': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.ipv4', + type: 'long', + }, + 'suricata.eve.stats.decoder.ipv6': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.ipv6', + type: 'long', + }, + 'suricata.eve.stats.decoder.pkts': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.pkts', + type: 'long', + }, + 'suricata.eve.stats.decoder.ipv6_in_ipv6': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.ipv6_in_ipv6', + type: 'long', + }, + 'suricata.eve.stats.decoder.ipraw.invalid_ip_version': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.ipraw.invalid_ip_version', + type: 'long', + }, + 'suricata.eve.stats.decoder.pppoe': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.pppoe', + type: 'long', + }, + 'suricata.eve.stats.decoder.udp': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.udp', + type: 'long', + }, + 'suricata.eve.stats.decoder.dce.pkt_too_small': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.dce.pkt_too_small', + type: 'long', + }, + 'suricata.eve.stats.decoder.vlan': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.vlan', + type: 'long', + }, + 'suricata.eve.stats.decoder.sctp': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.sctp', + type: 'long', + }, + 'suricata.eve.stats.decoder.max_pkt_size': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.max_pkt_size', + type: 'long', + }, + 'suricata.eve.stats.decoder.teredo': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.teredo', + type: 'long', + }, + 'suricata.eve.stats.decoder.mpls': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.mpls', + type: 'long', + }, + 'suricata.eve.stats.decoder.sll': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.sll', + type: 'long', + }, + 'suricata.eve.stats.decoder.icmpv6': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.icmpv6', + type: 'long', + }, + 'suricata.eve.stats.decoder.icmpv4': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.icmpv4', + type: 'long', + }, + 'suricata.eve.stats.decoder.erspan': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.erspan', + type: 'long', + }, + 'suricata.eve.stats.decoder.ethernet': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.ethernet', + type: 'long', + }, + 'suricata.eve.stats.decoder.ipv4_in_ipv6': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.ipv4_in_ipv6', + type: 'long', + }, + 'suricata.eve.stats.decoder.ieee8021ah': { + category: 'suricata', + name: 'suricata.eve.stats.decoder.ieee8021ah', + type: 'long', + }, + 'suricata.eve.stats.dns.memcap_global': { + category: 'suricata', + name: 'suricata.eve.stats.dns.memcap_global', + type: 'long', + }, + 'suricata.eve.stats.dns.memcap_state': { + category: 'suricata', + name: 'suricata.eve.stats.dns.memcap_state', + type: 'long', + }, + 'suricata.eve.stats.dns.memuse': { + category: 'suricata', + name: 'suricata.eve.stats.dns.memuse', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.rows_busy': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.rows_busy', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.flows_timeout': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.flows_timeout', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.flows_notimeout': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.flows_notimeout', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.rows_skipped': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.rows_skipped', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.closed_pruned': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.closed_pruned', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.new_pruned': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.new_pruned', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.flows_removed': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.flows_removed', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.bypassed_pruned': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.bypassed_pruned', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.est_pruned': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.est_pruned', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.flows_timeout_inuse': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.flows_timeout_inuse', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.flows_checked': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.flows_checked', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.rows_maxlen': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.rows_maxlen', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.rows_checked': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.rows_checked', + type: 'long', + }, + 'suricata.eve.stats.flow_mgr.rows_empty': { + category: 'suricata', + name: 'suricata.eve.stats.flow_mgr.rows_empty', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.tls': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.tls', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.ftp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.ftp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.http': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.http', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.failed_udp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.failed_udp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.dns_udp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.dns_udp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.dns_tcp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.dns_tcp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.smtp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.smtp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.failed_tcp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.failed_tcp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.msn': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.msn', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.ssh': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.ssh', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.imap': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.imap', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.dcerpc_udp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.dcerpc_udp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.dcerpc_tcp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.dcerpc_tcp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.flow.smb': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.flow.smb', + type: 'long', + }, + 'suricata.eve.stats.app_layer.tx.tls': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.tx.tls', + type: 'long', + }, + 'suricata.eve.stats.app_layer.tx.ftp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.tx.ftp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.tx.http': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.tx.http', + type: 'long', + }, + 'suricata.eve.stats.app_layer.tx.dns_udp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.tx.dns_udp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.tx.dns_tcp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.tx.dns_tcp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.tx.smtp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.tx.smtp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.tx.ssh': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.tx.ssh', + type: 'long', + }, + 'suricata.eve.stats.app_layer.tx.dcerpc_udp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.tx.dcerpc_udp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.tx.dcerpc_tcp': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.tx.dcerpc_tcp', + type: 'long', + }, + 'suricata.eve.stats.app_layer.tx.smb': { + category: 'suricata', + name: 'suricata.eve.stats.app_layer.tx.smb', + type: 'long', + }, + 'suricata.eve.tls.notbefore': { + category: 'suricata', + name: 'suricata.eve.tls.notbefore', + type: 'date', + }, + 'suricata.eve.tls.issuerdn': { + category: 'suricata', + name: 'suricata.eve.tls.issuerdn', + type: 'keyword', + }, + 'suricata.eve.tls.sni': { + category: 'suricata', + name: 'suricata.eve.tls.sni', + type: 'keyword', + }, + 'suricata.eve.tls.version': { + category: 'suricata', + name: 'suricata.eve.tls.version', + type: 'keyword', + }, + 'suricata.eve.tls.session_resumed': { + category: 'suricata', + name: 'suricata.eve.tls.session_resumed', + type: 'boolean', + }, + 'suricata.eve.tls.fingerprint': { + category: 'suricata', + name: 'suricata.eve.tls.fingerprint', + type: 'keyword', + }, + 'suricata.eve.tls.serial': { + category: 'suricata', + name: 'suricata.eve.tls.serial', + type: 'keyword', + }, + 'suricata.eve.tls.notafter': { + category: 'suricata', + name: 'suricata.eve.tls.notafter', + type: 'date', + }, + 'suricata.eve.tls.subject': { + category: 'suricata', + name: 'suricata.eve.tls.subject', + type: 'keyword', + }, + 'suricata.eve.tls.ja3s.string': { + category: 'suricata', + name: 'suricata.eve.tls.ja3s.string', + type: 'keyword', + }, + 'suricata.eve.tls.ja3s.hash': { + category: 'suricata', + name: 'suricata.eve.tls.ja3s.hash', + type: 'keyword', + }, + 'suricata.eve.tls.ja3.string': { + category: 'suricata', + name: 'suricata.eve.tls.ja3.string', + type: 'keyword', + }, + 'suricata.eve.tls.ja3.hash': { + category: 'suricata', + name: 'suricata.eve.tls.ja3.hash', + type: 'keyword', + }, + 'suricata.eve.app_proto_ts': { + category: 'suricata', + name: 'suricata.eve.app_proto_ts', + type: 'keyword', + }, + 'suricata.eve.flow.bytes_toclient': { + category: 'suricata', + name: 'suricata.eve.flow.bytes_toclient', + type: 'alias', + }, + 'suricata.eve.flow.start': { + category: 'suricata', + name: 'suricata.eve.flow.start', + type: 'alias', + }, + 'suricata.eve.flow.pkts_toclient': { + category: 'suricata', + name: 'suricata.eve.flow.pkts_toclient', + type: 'alias', + }, + 'suricata.eve.flow.age': { + category: 'suricata', + name: 'suricata.eve.flow.age', + type: 'long', + }, + 'suricata.eve.flow.state': { + category: 'suricata', + name: 'suricata.eve.flow.state', + type: 'keyword', + }, + 'suricata.eve.flow.bytes_toserver': { + category: 'suricata', + name: 'suricata.eve.flow.bytes_toserver', + type: 'alias', + }, + 'suricata.eve.flow.reason': { + category: 'suricata', + name: 'suricata.eve.flow.reason', + type: 'keyword', + }, + 'suricata.eve.flow.pkts_toserver': { + category: 'suricata', + name: 'suricata.eve.flow.pkts_toserver', + type: 'alias', + }, + 'suricata.eve.flow.end': { + category: 'suricata', + name: 'suricata.eve.flow.end', + type: 'date', + }, + 'suricata.eve.flow.alerted': { + category: 'suricata', + name: 'suricata.eve.flow.alerted', + type: 'boolean', + }, + 'suricata.eve.app_proto': { + category: 'suricata', + name: 'suricata.eve.app_proto', + type: 'alias', + }, + 'suricata.eve.tx_id': { + category: 'suricata', + name: 'suricata.eve.tx_id', + type: 'long', + }, + 'suricata.eve.app_proto_tc': { + category: 'suricata', + name: 'suricata.eve.app_proto_tc', + type: 'keyword', + }, + 'suricata.eve.smtp.rcpt_to': { + category: 'suricata', + name: 'suricata.eve.smtp.rcpt_to', + type: 'keyword', + }, + 'suricata.eve.smtp.mail_from': { + category: 'suricata', + name: 'suricata.eve.smtp.mail_from', + type: 'keyword', + }, + 'suricata.eve.smtp.helo': { + category: 'suricata', + name: 'suricata.eve.smtp.helo', + type: 'keyword', + }, + 'suricata.eve.app_proto_expected': { + category: 'suricata', + name: 'suricata.eve.app_proto_expected', + type: 'keyword', + }, + 'suricata.eve.flags': { + category: 'suricata', + name: 'suricata.eve.flags', + type: 'group', + }, + 'zeek.session_id': { + category: 'zeek', + description: 'A unique identifier of the session ', + name: 'zeek.session_id', + type: 'keyword', + }, + 'zeek.capture_loss.ts_delta': { + category: 'zeek', + description: 'The time delay between this measurement and the last. ', + name: 'zeek.capture_loss.ts_delta', + type: 'integer', + }, + 'zeek.capture_loss.peer': { + category: 'zeek', + description: + 'In the event that there are multiple Bro instances logging to the same host, this distinguishes each peer with its individual name. ', + name: 'zeek.capture_loss.peer', + type: 'keyword', + }, + 'zeek.capture_loss.gaps': { + category: 'zeek', + description: 'Number of missed ACKs from the previous measurement interval. ', + name: 'zeek.capture_loss.gaps', + type: 'integer', + }, + 'zeek.capture_loss.acks': { + category: 'zeek', + description: 'Total number of ACKs seen in the previous measurement interval. ', + name: 'zeek.capture_loss.acks', + type: 'integer', + }, + 'zeek.capture_loss.percent_lost': { + category: 'zeek', + description: "Percentage of ACKs seen where the data being ACKed wasn't seen. ", + name: 'zeek.capture_loss.percent_lost', + type: 'double', + }, + 'zeek.connection.local_orig': { + category: 'zeek', + description: 'Indicates whether the session is originated locally. ', + name: 'zeek.connection.local_orig', + type: 'boolean', + }, + 'zeek.connection.local_resp': { + category: 'zeek', + description: 'Indicates whether the session is responded locally. ', + name: 'zeek.connection.local_resp', + type: 'boolean', + }, + 'zeek.connection.missed_bytes': { + category: 'zeek', + description: 'Missed bytes for the session. ', + name: 'zeek.connection.missed_bytes', + type: 'long', + }, + 'zeek.connection.state': { + category: 'zeek', + description: 'Code indicating the state of the session. ', + name: 'zeek.connection.state', + type: 'keyword', + }, + 'zeek.connection.state_message': { + category: 'zeek', + description: 'The state of the session. ', + name: 'zeek.connection.state_message', + type: 'keyword', + }, + 'zeek.connection.icmp.type': { + category: 'zeek', + description: 'ICMP message type. ', + name: 'zeek.connection.icmp.type', + type: 'integer', + }, + 'zeek.connection.icmp.code': { + category: 'zeek', + description: 'ICMP message code. ', + name: 'zeek.connection.icmp.code', + type: 'integer', + }, + 'zeek.connection.history': { + category: 'zeek', + description: 'Flags indicating the history of the session. ', + name: 'zeek.connection.history', + type: 'keyword', + }, + 'zeek.connection.vlan': { + category: 'zeek', + description: 'VLAN identifier. ', + name: 'zeek.connection.vlan', + type: 'integer', + }, + 'zeek.connection.inner_vlan': { + category: 'zeek', + description: 'VLAN identifier. ', + name: 'zeek.connection.inner_vlan', + type: 'integer', + }, + 'zeek.dce_rpc.rtt': { + category: 'zeek', + description: + "Round trip time from the request to the response. If either the request or response wasn't seen, this will be null. ", + name: 'zeek.dce_rpc.rtt', + type: 'integer', + }, + 'zeek.dce_rpc.named_pipe': { + category: 'zeek', + description: 'Remote pipe name. ', + name: 'zeek.dce_rpc.named_pipe', + type: 'keyword', + }, + 'zeek.dce_rpc.endpoint': { + category: 'zeek', + description: 'Endpoint name looked up from the uuid. ', + name: 'zeek.dce_rpc.endpoint', + type: 'keyword', + }, + 'zeek.dce_rpc.operation': { + category: 'zeek', + description: 'Operation seen in the call. ', + name: 'zeek.dce_rpc.operation', + type: 'keyword', + }, + 'zeek.dhcp.domain': { + category: 'zeek', + description: 'Domain given by the server in option 15. ', + name: 'zeek.dhcp.domain', + type: 'keyword', + }, + 'zeek.dhcp.duration': { + category: 'zeek', + description: + 'Duration of the DHCP session representing the time from the first message to the last, in seconds. ', + name: 'zeek.dhcp.duration', + type: 'double', + }, + 'zeek.dhcp.hostname': { + category: 'zeek', + description: 'Name given by client in Hostname option 12. ', + name: 'zeek.dhcp.hostname', + type: 'keyword', + }, + 'zeek.dhcp.client_fqdn': { + category: 'zeek', + description: 'FQDN given by client in Client FQDN option 81. ', + name: 'zeek.dhcp.client_fqdn', + type: 'keyword', + }, + 'zeek.dhcp.lease_time': { + category: 'zeek', + description: 'IP address lease interval in seconds. ', + name: 'zeek.dhcp.lease_time', + type: 'integer', + }, + 'zeek.dhcp.address.assigned': { + category: 'zeek', + description: 'IP address assigned by the server. ', + name: 'zeek.dhcp.address.assigned', + type: 'ip', + }, + 'zeek.dhcp.address.client': { + category: 'zeek', + description: + 'IP address of the client. If a transaction is only a client sending INFORM messages then there is no lease information exchanged so this is helpful to know who sent the messages. Getting an address in this field does require that the client sources at least one DHCP message using a non-broadcast address. ', + name: 'zeek.dhcp.address.client', + type: 'ip', + }, + 'zeek.dhcp.address.mac': { + category: 'zeek', + description: "Client's hardware address. ", + name: 'zeek.dhcp.address.mac', + type: 'keyword', + }, + 'zeek.dhcp.address.requested': { + category: 'zeek', + description: 'IP address requested by the client. ', + name: 'zeek.dhcp.address.requested', + type: 'ip', + }, + 'zeek.dhcp.address.server': { + category: 'zeek', + description: 'IP address of the DHCP server. ', + name: 'zeek.dhcp.address.server', + type: 'ip', + }, + 'zeek.dhcp.msg.types': { + category: 'zeek', + description: 'List of DHCP message types seen in this exchange. ', + name: 'zeek.dhcp.msg.types', + type: 'keyword', + }, + 'zeek.dhcp.msg.origin': { + category: 'zeek', + description: + '(present if policy/protocols/dhcp/msg-orig.bro is loaded) The address that originated each message from the msg.types field. ', + name: 'zeek.dhcp.msg.origin', + type: 'ip', + }, + 'zeek.dhcp.msg.client': { + category: 'zeek', + description: + 'Message typically accompanied with a DHCP_DECLINE so the client can tell the server why it rejected an address. ', + name: 'zeek.dhcp.msg.client', + type: 'keyword', + }, + 'zeek.dhcp.msg.server': { + category: 'zeek', + description: + 'Message typically accompanied with a DHCP_NAK to let the client know why it rejected the request. ', + name: 'zeek.dhcp.msg.server', + type: 'keyword', + }, + 'zeek.dhcp.software.client': { + category: 'zeek', + description: + '(present if policy/protocols/dhcp/software.bro is loaded) Software reported by the client in the vendor_class option. ', + name: 'zeek.dhcp.software.client', + type: 'keyword', + }, + 'zeek.dhcp.software.server': { + category: 'zeek', + description: + '(present if policy/protocols/dhcp/software.bro is loaded) Software reported by the client in the vendor_class option. ', + name: 'zeek.dhcp.software.server', + type: 'keyword', + }, + 'zeek.dhcp.id.circuit': { + category: 'zeek', + description: + '(present if policy/protocols/dhcp/sub-opts.bro is loaded) Added by DHCP relay agents which terminate switched or permanent circuits. It encodes an agent-local identifier of the circuit from which a DHCP client-to-server packet was received. Typically it should represent a router or switch interface number. ', + name: 'zeek.dhcp.id.circuit', + type: 'keyword', + }, + 'zeek.dhcp.id.remote_agent': { + category: 'zeek', + description: + '(present if policy/protocols/dhcp/sub-opts.bro is loaded) A globally unique identifier added by relay agents to identify the remote host end of the circuit. ', + name: 'zeek.dhcp.id.remote_agent', + type: 'keyword', + }, + 'zeek.dhcp.id.subscriber': { + category: 'zeek', + description: + "(present if policy/protocols/dhcp/sub-opts.bro is loaded) The subscriber ID is a value independent of the physical network configuration so that a customer's DHCP configuration can be given to them correctly no matter where they are physically connected. ", + name: 'zeek.dhcp.id.subscriber', + type: 'keyword', + }, + 'zeek.dnp3.function.request': { + category: 'zeek', + description: 'The name of the function message in the request. ', + name: 'zeek.dnp3.function.request', + type: 'keyword', + }, + 'zeek.dnp3.function.reply': { + category: 'zeek', + description: 'The name of the function message in the reply. ', + name: 'zeek.dnp3.function.reply', + type: 'keyword', + }, + 'zeek.dnp3.id': { + category: 'zeek', + description: "The response's internal indication number. ", + name: 'zeek.dnp3.id', + type: 'integer', + }, + 'zeek.dns.trans_id': { + category: 'zeek', + description: 'DNS transaction identifier. ', + name: 'zeek.dns.trans_id', + type: 'keyword', + }, + 'zeek.dns.rtt': { + category: 'zeek', + description: 'Round trip time for the query and response. ', + name: 'zeek.dns.rtt', + type: 'double', + }, + 'zeek.dns.query': { + category: 'zeek', + description: 'The domain name that is the subject of the DNS query. ', + name: 'zeek.dns.query', + type: 'keyword', + }, + 'zeek.dns.qclass': { + category: 'zeek', + description: 'The QCLASS value specifying the class of the query. ', + name: 'zeek.dns.qclass', + type: 'long', + }, + 'zeek.dns.qclass_name': { + category: 'zeek', + description: 'A descriptive name for the class of the query. ', + name: 'zeek.dns.qclass_name', + type: 'keyword', + }, + 'zeek.dns.qtype': { + category: 'zeek', + description: 'A QTYPE value specifying the type of the query. ', + name: 'zeek.dns.qtype', + type: 'long', + }, + 'zeek.dns.qtype_name': { + category: 'zeek', + description: 'A descriptive name for the type of the query. ', + name: 'zeek.dns.qtype_name', + type: 'keyword', + }, + 'zeek.dns.rcode': { + category: 'zeek', + description: 'The response code value in DNS response messages. ', + name: 'zeek.dns.rcode', + type: 'long', + }, + 'zeek.dns.rcode_name': { + category: 'zeek', + description: 'A descriptive name for the response code value. ', + name: 'zeek.dns.rcode_name', + type: 'keyword', + }, + 'zeek.dns.AA': { + category: 'zeek', + description: + 'The Authoritative Answer bit for response messages specifies that the responding name server is an authority for the domain name in the question section. ', + name: 'zeek.dns.AA', + type: 'boolean', + }, + 'zeek.dns.TC': { + category: 'zeek', + description: 'The Truncation bit specifies that the message was truncated. ', + name: 'zeek.dns.TC', + type: 'boolean', + }, + 'zeek.dns.RD': { + category: 'zeek', + description: + 'The Recursion Desired bit in a request message indicates that the client wants recursive service for this query. ', + name: 'zeek.dns.RD', + type: 'boolean', + }, + 'zeek.dns.RA': { + category: 'zeek', + description: + 'The Recursion Available bit in a response message indicates that the name server supports recursive queries. ', + name: 'zeek.dns.RA', + type: 'boolean', + }, + 'zeek.dns.answers': { + category: 'zeek', + description: 'The set of resource descriptions in the query answer. ', + name: 'zeek.dns.answers', + type: 'keyword', + }, + 'zeek.dns.TTLs': { + category: 'zeek', + description: 'The caching intervals of the associated RRs described by the answers field. ', + name: 'zeek.dns.TTLs', + type: 'double', + }, + 'zeek.dns.rejected': { + category: 'zeek', + description: 'Indicates whether the DNS query was rejected by the server. ', + name: 'zeek.dns.rejected', + type: 'boolean', + }, + 'zeek.dns.total_answers': { + category: 'zeek', + description: 'The total number of resource records in the reply. ', + name: 'zeek.dns.total_answers', + type: 'integer', + }, + 'zeek.dns.total_replies': { + category: 'zeek', + description: 'The total number of resource records in the reply message. ', + name: 'zeek.dns.total_replies', + type: 'integer', + }, + 'zeek.dns.saw_query': { + category: 'zeek', + description: 'Whether the full DNS query has been seen. ', + name: 'zeek.dns.saw_query', + type: 'boolean', + }, + 'zeek.dns.saw_reply': { + category: 'zeek', + description: 'Whether the full DNS reply has been seen. ', + name: 'zeek.dns.saw_reply', + type: 'boolean', + }, + 'zeek.dpd.analyzer': { + category: 'zeek', + description: 'The analyzer that generated the violation. ', + name: 'zeek.dpd.analyzer', + type: 'keyword', + }, + 'zeek.dpd.failure_reason': { + category: 'zeek', + description: 'The textual reason for the analysis failure. ', + name: 'zeek.dpd.failure_reason', + type: 'keyword', + }, + 'zeek.dpd.packet_segment': { + category: 'zeek', + description: + '(present if policy/frameworks/dpd/packet-segment-logging.bro is loaded) A chunk of the payload that most likely resulted in the protocol violation. ', + name: 'zeek.dpd.packet_segment', + type: 'keyword', + }, + 'zeek.files.fuid': { + category: 'zeek', + description: 'A file unique identifier. ', + name: 'zeek.files.fuid', + type: 'keyword', + }, + 'zeek.files.tx_host': { + category: 'zeek', + description: 'The host that transferred the file. ', + name: 'zeek.files.tx_host', + type: 'ip', + }, + 'zeek.files.rx_host': { + category: 'zeek', + description: 'The host that received the file. ', + name: 'zeek.files.rx_host', + type: 'ip', + }, + 'zeek.files.session_ids': { + category: 'zeek', + description: 'The sessions that have this file. ', + name: 'zeek.files.session_ids', + type: 'keyword', + }, + 'zeek.files.source': { + category: 'zeek', + description: + 'An identification of the source of the file data. E.g. it may be a network protocol over which it was transferred, or a local file path which was read, or some other input source. ', + name: 'zeek.files.source', + type: 'keyword', + }, + 'zeek.files.depth': { + category: 'zeek', + description: + 'A value to represent the depth of this file in relation to its source. In SMTP, it is the depth of the MIME attachment on the message. In HTTP, it is the depth of the request within the TCP connection. ', + name: 'zeek.files.depth', + type: 'long', + }, + 'zeek.files.analyzers': { + category: 'zeek', + description: 'A set of analysis types done during the file analysis. ', + name: 'zeek.files.analyzers', + type: 'keyword', + }, + 'zeek.files.mime_type': { + category: 'zeek', + description: 'Mime type of the file. ', + name: 'zeek.files.mime_type', + type: 'keyword', + }, + 'zeek.files.filename': { + category: 'zeek', + description: 'Name of the file if available. ', + name: 'zeek.files.filename', + type: 'keyword', + }, + 'zeek.files.local_orig': { + category: 'zeek', + description: + 'If the source of this file is a network connection, this field indicates if the data originated from the local network or not. ', + name: 'zeek.files.local_orig', + type: 'boolean', + }, + 'zeek.files.is_orig': { + category: 'zeek', + description: + 'If the source of this file is a network connection, this field indicates if the file is being sent by the originator of the connection or the responder. ', + name: 'zeek.files.is_orig', + type: 'boolean', + }, + 'zeek.files.duration': { + category: 'zeek', + description: 'The duration the file was analyzed for. Not the duration of the session. ', + name: 'zeek.files.duration', + type: 'double', + }, + 'zeek.files.seen_bytes': { + category: 'zeek', + description: 'Number of bytes provided to the file analysis engine for the file. ', + name: 'zeek.files.seen_bytes', + type: 'long', + }, + 'zeek.files.total_bytes': { + category: 'zeek', + description: 'Total number of bytes that are supposed to comprise the full file. ', + name: 'zeek.files.total_bytes', + type: 'long', + }, + 'zeek.files.missing_bytes': { + category: 'zeek', + description: + 'The number of bytes in the file stream that were completely missed during the process of analysis. ', + name: 'zeek.files.missing_bytes', + type: 'long', + }, + 'zeek.files.overflow_bytes': { + category: 'zeek', + description: + "The number of bytes in the file stream that were not delivered to stream file analyzers. This could be overlapping bytes or bytes that couldn't be reassembled. ", + name: 'zeek.files.overflow_bytes', + type: 'long', + }, + 'zeek.files.timedout': { + category: 'zeek', + description: 'Whether the file analysis timed out at least once for the file. ', + name: 'zeek.files.timedout', + type: 'boolean', + }, + 'zeek.files.parent_fuid': { + category: 'zeek', + description: + 'Identifier associated with a container file from which this one was extracted as part of the file analysis. ', + name: 'zeek.files.parent_fuid', + type: 'keyword', + }, + 'zeek.files.md5': { + category: 'zeek', + description: 'An MD5 digest of the file contents. ', + name: 'zeek.files.md5', + type: 'keyword', + }, + 'zeek.files.sha1': { + category: 'zeek', + description: 'A SHA1 digest of the file contents. ', + name: 'zeek.files.sha1', + type: 'keyword', + }, + 'zeek.files.sha256': { + category: 'zeek', + description: 'A SHA256 digest of the file contents. ', + name: 'zeek.files.sha256', + type: 'keyword', + }, + 'zeek.files.extracted': { + category: 'zeek', + description: 'Local filename of extracted file. ', + name: 'zeek.files.extracted', + type: 'keyword', + }, + 'zeek.files.extracted_cutoff': { + category: 'zeek', + description: + 'Indicate whether the file being extracted was cut off hence not extracted completely. ', + name: 'zeek.files.extracted_cutoff', + type: 'boolean', + }, + 'zeek.files.extracted_size': { + category: 'zeek', + description: 'The number of bytes extracted to disk. ', + name: 'zeek.files.extracted_size', + type: 'long', + }, + 'zeek.files.entropy': { + category: 'zeek', + description: 'The information density of the contents of the file. ', + name: 'zeek.files.entropy', + type: 'double', + }, + 'zeek.ftp.user': { + category: 'zeek', + description: 'User name for the current FTP session. ', + name: 'zeek.ftp.user', + type: 'keyword', + }, + 'zeek.ftp.password': { + category: 'zeek', + description: 'Password for the current FTP session if captured. ', + name: 'zeek.ftp.password', + type: 'keyword', + }, + 'zeek.ftp.command': { + category: 'zeek', + description: 'Command given by the client. ', + name: 'zeek.ftp.command', + type: 'keyword', + }, + 'zeek.ftp.arg': { + category: 'zeek', + description: 'Argument for the command if one is given. ', + name: 'zeek.ftp.arg', + type: 'keyword', + }, + 'zeek.ftp.file.size': { + category: 'zeek', + description: 'Size of the file if the command indicates a file transfer. ', + name: 'zeek.ftp.file.size', + type: 'long', + }, + 'zeek.ftp.file.mime_type': { + category: 'zeek', + description: 'Sniffed mime type of file. ', + name: 'zeek.ftp.file.mime_type', + type: 'keyword', + }, + 'zeek.ftp.file.fuid': { + category: 'zeek', + description: '(present if base/protocols/ftp/files.bro is loaded) File unique ID. ', + name: 'zeek.ftp.file.fuid', + type: 'keyword', + }, + 'zeek.ftp.reply.code': { + category: 'zeek', + description: 'Reply code from the server in response to the command. ', + name: 'zeek.ftp.reply.code', + type: 'integer', + }, + 'zeek.ftp.reply.msg': { + category: 'zeek', + description: 'Reply message from the server in response to the command. ', + name: 'zeek.ftp.reply.msg', + type: 'keyword', + }, + 'zeek.ftp.data_channel.passive': { + category: 'zeek', + description: 'Whether PASV mode is toggled for control channel. ', + name: 'zeek.ftp.data_channel.passive', + type: 'boolean', + }, + 'zeek.ftp.data_channel.originating_host': { + category: 'zeek', + description: 'The host that will be initiating the data connection. ', + name: 'zeek.ftp.data_channel.originating_host', + type: 'ip', + }, + 'zeek.ftp.data_channel.response_host': { + category: 'zeek', + description: 'The host that will be accepting the data connection. ', + name: 'zeek.ftp.data_channel.response_host', + type: 'ip', + }, + 'zeek.ftp.data_channel.response_port': { + category: 'zeek', + description: 'The port at which the acceptor is listening for the data connection. ', + name: 'zeek.ftp.data_channel.response_port', + type: 'integer', + }, + 'zeek.ftp.cwd': { + category: 'zeek', + description: + "Current working directory that this session is in. By making the default value '.', we can indicate that unless something more concrete is discovered that the existing but unknown directory is ok to use. ", + name: 'zeek.ftp.cwd', + type: 'keyword', + }, + 'zeek.ftp.cmdarg.cmd': { + category: 'zeek', + description: 'Command. ', + name: 'zeek.ftp.cmdarg.cmd', + type: 'keyword', + }, + 'zeek.ftp.cmdarg.arg': { + category: 'zeek', + description: 'Argument for the command if one was given. ', + name: 'zeek.ftp.cmdarg.arg', + type: 'keyword', + }, + 'zeek.ftp.cmdarg.seq': { + category: 'zeek', + description: 'Counter to track how many commands have been executed. ', + name: 'zeek.ftp.cmdarg.seq', + type: 'integer', + }, + 'zeek.ftp.pending_commands': { + category: 'zeek', + description: + 'Queue for commands that have been sent but not yet responded to are tracked here. ', + name: 'zeek.ftp.pending_commands', + type: 'integer', + }, + 'zeek.ftp.passive': { + category: 'zeek', + description: 'Indicates if the session is in active or passive mode. ', + name: 'zeek.ftp.passive', + type: 'boolean', + }, + 'zeek.ftp.capture_password': { + category: 'zeek', + description: 'Determines if the password will be captured for this request. ', + name: 'zeek.ftp.capture_password', + type: 'boolean', + }, + 'zeek.ftp.last_auth_requested': { + category: 'zeek', + description: + 'present if base/protocols/ftp/gridftp.bro is loaded. Last authentication/security mechanism that was used. ', + name: 'zeek.ftp.last_auth_requested', + type: 'keyword', + }, + 'zeek.http.trans_depth': { + category: 'zeek', + description: + 'Represents the pipelined depth into the connection of this request/response transaction. ', + name: 'zeek.http.trans_depth', + type: 'integer', + }, + 'zeek.http.status_msg': { + category: 'zeek', + description: 'Status message returned by the server. ', + name: 'zeek.http.status_msg', + type: 'keyword', + }, + 'zeek.http.info_code': { + category: 'zeek', + description: 'Last seen 1xx informational reply code returned by the server. ', + name: 'zeek.http.info_code', + type: 'integer', + }, + 'zeek.http.info_msg': { + category: 'zeek', + description: 'Last seen 1xx informational reply message returned by the server. ', + name: 'zeek.http.info_msg', + type: 'keyword', + }, + 'zeek.http.tags': { + category: 'zeek', + description: + 'A set of indicators of various attributes discovered and related to a particular request/response pair. ', + name: 'zeek.http.tags', + type: 'keyword', + }, + 'zeek.http.password': { + category: 'zeek', + description: 'Password if basic-auth is performed for the request. ', + name: 'zeek.http.password', + type: 'keyword', + }, + 'zeek.http.captured_password': { + category: 'zeek', + description: 'Determines if the password will be captured for this request. ', + name: 'zeek.http.captured_password', + type: 'boolean', + }, + 'zeek.http.proxied': { + category: 'zeek', + description: 'All of the headers that may indicate if the HTTP request was proxied. ', + name: 'zeek.http.proxied', + type: 'keyword', + }, + 'zeek.http.range_request': { + category: 'zeek', + description: 'Indicates if this request can assume 206 partial content in response. ', + name: 'zeek.http.range_request', + type: 'boolean', + }, + 'zeek.http.client_header_names': { + category: 'zeek', + description: + 'The vector of HTTP header names sent by the client. No header values are included here, just the header names. ', + name: 'zeek.http.client_header_names', + type: 'keyword', + }, + 'zeek.http.server_header_names': { + category: 'zeek', + description: + 'The vector of HTTP header names sent by the server. No header values are included here, just the header names. ', + name: 'zeek.http.server_header_names', + type: 'keyword', + }, + 'zeek.http.orig_fuids': { + category: 'zeek', + description: 'An ordered vector of file unique IDs from the originator. ', + name: 'zeek.http.orig_fuids', + type: 'keyword', + }, + 'zeek.http.orig_mime_types': { + category: 'zeek', + description: 'An ordered vector of mime types from the originator. ', + name: 'zeek.http.orig_mime_types', + type: 'keyword', + }, + 'zeek.http.orig_filenames': { + category: 'zeek', + description: 'An ordered vector of filenames from the originator. ', + name: 'zeek.http.orig_filenames', + type: 'keyword', + }, + 'zeek.http.resp_fuids': { + category: 'zeek', + description: 'An ordered vector of file unique IDs from the responder. ', + name: 'zeek.http.resp_fuids', + type: 'keyword', + }, + 'zeek.http.resp_mime_types': { + category: 'zeek', + description: 'An ordered vector of mime types from the responder. ', + name: 'zeek.http.resp_mime_types', + type: 'keyword', + }, + 'zeek.http.resp_filenames': { + category: 'zeek', + description: 'An ordered vector of filenames from the responder. ', + name: 'zeek.http.resp_filenames', + type: 'keyword', + }, + 'zeek.http.orig_mime_depth': { + category: 'zeek', + description: 'Current number of MIME entities in the HTTP request message body. ', + name: 'zeek.http.orig_mime_depth', + type: 'integer', + }, + 'zeek.http.resp_mime_depth': { + category: 'zeek', + description: 'Current number of MIME entities in the HTTP response message body. ', + name: 'zeek.http.resp_mime_depth', + type: 'integer', + }, + 'zeek.intel.seen.indicator': { + category: 'zeek', + description: 'The intelligence indicator. ', + name: 'zeek.intel.seen.indicator', + type: 'keyword', + }, + 'zeek.intel.seen.indicator_type': { + category: 'zeek', + description: 'The type of data the indicator represents. ', + name: 'zeek.intel.seen.indicator_type', + type: 'keyword', + }, + 'zeek.intel.seen.host': { + category: 'zeek', + description: 'If the indicator type was Intel::ADDR, then this field will be present. ', + name: 'zeek.intel.seen.host', + type: 'keyword', + }, + 'zeek.intel.seen.conn': { + category: 'zeek', + description: + 'If the data was discovered within a connection, the connection record should go here to give context to the data. ', + name: 'zeek.intel.seen.conn', + type: 'keyword', + }, + 'zeek.intel.seen.where': { + category: 'zeek', + description: 'Where the data was discovered. ', + name: 'zeek.intel.seen.where', + type: 'keyword', + }, + 'zeek.intel.seen.node': { + category: 'zeek', + description: 'The name of the node where the match was discovered. ', + name: 'zeek.intel.seen.node', + type: 'keyword', + }, + 'zeek.intel.seen.uid': { + category: 'zeek', + description: + 'If the data was discovered within a connection, the connection uid should go here to give context to the data. If the conn field is provided, this will be automatically filled out. ', + name: 'zeek.intel.seen.uid', + type: 'keyword', + }, + 'zeek.intel.seen.f': { + category: 'zeek', + description: + 'If the data was discovered within a file, the file record should go here to provide context to the data. ', + name: 'zeek.intel.seen.f', + type: 'object', + }, + 'zeek.intel.seen.fuid': { + category: 'zeek', + description: + 'If the data was discovered within a file, the file uid should go here to provide context to the data. If the file record f is provided, this will be automatically filled out. ', + name: 'zeek.intel.seen.fuid', + type: 'keyword', + }, + 'zeek.intel.matched': { + category: 'zeek', + description: 'Event to represent a match in the intelligence data from data that was seen. ', + name: 'zeek.intel.matched', + type: 'keyword', + }, + 'zeek.intel.sources': { + category: 'zeek', + description: 'Sources which supplied data for this match. ', + name: 'zeek.intel.sources', + type: 'keyword', + }, + 'zeek.intel.fuid': { + category: 'zeek', + description: + 'If a file was associated with this intelligence hit, this is the uid for the file. ', + name: 'zeek.intel.fuid', + type: 'keyword', + }, + 'zeek.intel.file_mime_type': { + category: 'zeek', + description: + 'A mime type if the intelligence hit is related to a file. If the $f field is provided this will be automatically filled out. ', + name: 'zeek.intel.file_mime_type', + type: 'keyword', + }, + 'zeek.intel.file_desc': { + category: 'zeek', + description: + 'Frequently files can be described to give a bit more context. If the $f field is provided this field will be automatically filled out. ', + name: 'zeek.intel.file_desc', + type: 'keyword', + }, + 'zeek.irc.nick': { + category: 'zeek', + description: 'Nickname given for the connection. ', + name: 'zeek.irc.nick', + type: 'keyword', + }, + 'zeek.irc.user': { + category: 'zeek', + description: 'Username given for the connection. ', + name: 'zeek.irc.user', + type: 'keyword', + }, + 'zeek.irc.command': { + category: 'zeek', + description: 'Command given by the client. ', + name: 'zeek.irc.command', + type: 'keyword', + }, + 'zeek.irc.value': { + category: 'zeek', + description: 'Value for the command given by the client. ', + name: 'zeek.irc.value', + type: 'keyword', + }, + 'zeek.irc.addl': { + category: 'zeek', + description: 'Any additional data for the command. ', + name: 'zeek.irc.addl', + type: 'keyword', + }, + 'zeek.irc.dcc.file.name': { + category: 'zeek', + description: 'Present if base/protocols/irc/dcc-send.bro is loaded. DCC filename requested. ', + name: 'zeek.irc.dcc.file.name', + type: 'keyword', + }, + 'zeek.irc.dcc.file.size': { + category: 'zeek', + description: + 'Present if base/protocols/irc/dcc-send.bro is loaded. Size of the DCC transfer as indicated by the sender. ', + name: 'zeek.irc.dcc.file.size', + type: 'long', + }, + 'zeek.irc.dcc.mime_type': { + category: 'zeek', + description: + 'present if base/protocols/irc/dcc-send.bro is loaded. Sniffed mime type of the file. ', + name: 'zeek.irc.dcc.mime_type', + type: 'keyword', + }, + 'zeek.irc.fuid': { + category: 'zeek', + description: 'present if base/protocols/irc/files.bro is loaded. File unique ID. ', + name: 'zeek.irc.fuid', + type: 'keyword', + }, + 'zeek.kerberos.request_type': { + category: 'zeek', + description: 'Request type - Authentication Service (AS) or Ticket Granting Service (TGS). ', + name: 'zeek.kerberos.request_type', + type: 'keyword', + }, + 'zeek.kerberos.client': { + category: 'zeek', + description: 'Client name. ', + name: 'zeek.kerberos.client', + type: 'keyword', + }, + 'zeek.kerberos.service': { + category: 'zeek', + description: 'Service name. ', + name: 'zeek.kerberos.service', + type: 'keyword', + }, + 'zeek.kerberos.success': { + category: 'zeek', + description: 'Request result. ', + name: 'zeek.kerberos.success', + type: 'boolean', + }, + 'zeek.kerberos.error.code': { + category: 'zeek', + description: 'Error code. ', + name: 'zeek.kerberos.error.code', + type: 'integer', + }, + 'zeek.kerberos.error.msg': { + category: 'zeek', + description: 'Error message. ', + name: 'zeek.kerberos.error.msg', + type: 'keyword', + }, + 'zeek.kerberos.valid.from': { + category: 'zeek', + description: 'Ticket valid from. ', + name: 'zeek.kerberos.valid.from', + type: 'date', + }, + 'zeek.kerberos.valid.until': { + category: 'zeek', + description: 'Ticket valid until. ', + name: 'zeek.kerberos.valid.until', + type: 'date', + }, + 'zeek.kerberos.valid.days': { + category: 'zeek', + description: 'Number of days the ticket is valid for. ', + name: 'zeek.kerberos.valid.days', + type: 'integer', + }, + 'zeek.kerberos.cipher': { + category: 'zeek', + description: 'Ticket encryption type. ', + name: 'zeek.kerberos.cipher', + type: 'keyword', + }, + 'zeek.kerberos.forwardable': { + category: 'zeek', + description: 'Forwardable ticket requested. ', + name: 'zeek.kerberos.forwardable', + type: 'boolean', + }, + 'zeek.kerberos.renewable': { + category: 'zeek', + description: 'Renewable ticket requested. ', + name: 'zeek.kerberos.renewable', + type: 'boolean', + }, + 'zeek.kerberos.ticket.auth': { + category: 'zeek', + description: 'Hash of ticket used to authorize request/transaction. ', + name: 'zeek.kerberos.ticket.auth', + type: 'keyword', + }, + 'zeek.kerberos.ticket.new': { + category: 'zeek', + description: 'Hash of ticket returned by the KDC. ', + name: 'zeek.kerberos.ticket.new', + type: 'keyword', + }, + 'zeek.kerberos.cert.client.value': { + category: 'zeek', + description: 'Client certificate. ', + name: 'zeek.kerberos.cert.client.value', + type: 'keyword', + }, + 'zeek.kerberos.cert.client.fuid': { + category: 'zeek', + description: 'File unique ID of client cert. ', + name: 'zeek.kerberos.cert.client.fuid', + type: 'keyword', + }, + 'zeek.kerberos.cert.client.subject': { + category: 'zeek', + description: 'Subject of client certificate. ', + name: 'zeek.kerberos.cert.client.subject', + type: 'keyword', + }, + 'zeek.kerberos.cert.server.value': { + category: 'zeek', + description: 'Server certificate. ', + name: 'zeek.kerberos.cert.server.value', + type: 'keyword', + }, + 'zeek.kerberos.cert.server.fuid': { + category: 'zeek', + description: 'File unique ID of server certificate. ', + name: 'zeek.kerberos.cert.server.fuid', + type: 'keyword', + }, + 'zeek.kerberos.cert.server.subject': { + category: 'zeek', + description: 'Subject of server certificate. ', + name: 'zeek.kerberos.cert.server.subject', + type: 'keyword', + }, + 'zeek.modbus.function': { + category: 'zeek', + description: 'The name of the function message that was sent. ', + name: 'zeek.modbus.function', + type: 'keyword', + }, + 'zeek.modbus.exception': { + category: 'zeek', + description: 'The exception if the response was a failure. ', + name: 'zeek.modbus.exception', + type: 'keyword', + }, + 'zeek.modbus.track_address': { + category: 'zeek', + description: + 'Present if policy/protocols/modbus/track-memmap.bro is loaded. Modbus track address. ', + name: 'zeek.modbus.track_address', + type: 'integer', + }, + 'zeek.mysql.cmd': { + category: 'zeek', + description: 'The command that was issued. ', + name: 'zeek.mysql.cmd', + type: 'keyword', + }, + 'zeek.mysql.arg': { + category: 'zeek', + description: 'The argument issued to the command. ', + name: 'zeek.mysql.arg', + type: 'keyword', + }, + 'zeek.mysql.success': { + category: 'zeek', + description: 'Whether the command succeeded. ', + name: 'zeek.mysql.success', + type: 'boolean', + }, + 'zeek.mysql.rows': { + category: 'zeek', + description: 'The number of affected rows, if any. ', + name: 'zeek.mysql.rows', + type: 'integer', + }, + 'zeek.mysql.response': { + category: 'zeek', + description: 'Server message, if any. ', + name: 'zeek.mysql.response', + type: 'keyword', + }, + 'zeek.notice.connection_id': { + category: 'zeek', + description: 'Identifier of the related connection session. ', + name: 'zeek.notice.connection_id', + type: 'keyword', + }, + 'zeek.notice.icmp_id': { + category: 'zeek', + description: 'Identifier of the related ICMP session. ', + name: 'zeek.notice.icmp_id', + type: 'keyword', + }, + 'zeek.notice.file.id': { + category: 'zeek', + description: 'An identifier associated with a single file that is related to this notice. ', + name: 'zeek.notice.file.id', + type: 'keyword', + }, + 'zeek.notice.file.parent_id': { + category: 'zeek', + description: 'Identifier associated with a container file from which this one was extracted. ', + name: 'zeek.notice.file.parent_id', + type: 'keyword', + }, + 'zeek.notice.file.source': { + category: 'zeek', + description: + 'An identification of the source of the file data. E.g. it may be a network protocol over which it was transferred, or a local file path which was read, or some other input source. ', + name: 'zeek.notice.file.source', + type: 'keyword', + }, + 'zeek.notice.file.mime_type': { + category: 'zeek', + description: 'A mime type if the notice is related to a file. ', + name: 'zeek.notice.file.mime_type', + type: 'keyword', + }, + 'zeek.notice.file.is_orig': { + category: 'zeek', + description: + 'If the source of this file is a network connection, this field indicates if the file is being sent by the originator of the connection or the responder. ', + name: 'zeek.notice.file.is_orig', + type: 'boolean', + }, + 'zeek.notice.file.seen_bytes': { + category: 'zeek', + description: 'Number of bytes provided to the file analysis engine for the file. ', + name: 'zeek.notice.file.seen_bytes', + type: 'long', + }, + 'zeek.notice.ffile.total_bytes': { + category: 'zeek', + description: 'Total number of bytes that are supposed to comprise the full file. ', + name: 'zeek.notice.ffile.total_bytes', + type: 'long', + }, + 'zeek.notice.file.missing_bytes': { + category: 'zeek', + description: + 'The number of bytes in the file stream that were completely missed during the process of analysis. ', + name: 'zeek.notice.file.missing_bytes', + type: 'long', + }, + 'zeek.notice.file.overflow_bytes': { + category: 'zeek', + description: + "The number of bytes in the file stream that were not delivered to stream file analyzers. This could be overlapping bytes or bytes that couldn't be reassembled. ", + name: 'zeek.notice.file.overflow_bytes', + type: 'long', + }, + 'zeek.notice.fuid': { + category: 'zeek', + description: 'A file unique ID if this notice is related to a file. ', + name: 'zeek.notice.fuid', + type: 'keyword', + }, + 'zeek.notice.note': { + category: 'zeek', + description: 'The type of the notice. ', + name: 'zeek.notice.note', + type: 'keyword', + }, + 'zeek.notice.msg': { + category: 'zeek', + description: 'The human readable message for the notice. ', + name: 'zeek.notice.msg', + type: 'keyword', + }, + 'zeek.notice.sub': { + category: 'zeek', + description: 'The human readable sub-message. ', + name: 'zeek.notice.sub', + type: 'keyword', + }, + 'zeek.notice.n': { + category: 'zeek', + description: 'Associated count, or a status code. ', + name: 'zeek.notice.n', + type: 'long', + }, + 'zeek.notice.peer_name': { + category: 'zeek', + description: 'Name of remote peer that raised this notice. ', + name: 'zeek.notice.peer_name', + type: 'keyword', + }, + 'zeek.notice.peer_descr': { + category: 'zeek', + description: 'Textual description for the peer that raised this notice. ', + name: 'zeek.notice.peer_descr', + type: 'text', + }, + 'zeek.notice.actions': { + category: 'zeek', + description: 'The actions which have been applied to this notice. ', + name: 'zeek.notice.actions', + type: 'keyword', + }, + 'zeek.notice.email_body_sections': { + category: 'zeek', + description: + 'By adding chunks of text into this element, other scripts can expand on notices that are being emailed. ', + name: 'zeek.notice.email_body_sections', + type: 'text', + }, + 'zeek.notice.email_delay_tokens': { + category: 'zeek', + description: + 'Adding a string token to this set will cause the built-in emailing functionality to delay sending the email either the token has been removed or the email has been delayed for the specified time duration. ', + name: 'zeek.notice.email_delay_tokens', + type: 'keyword', + }, + 'zeek.notice.identifier': { + category: 'zeek', + description: + 'This field is provided when a notice is generated for the purpose of deduplicating notices. ', + name: 'zeek.notice.identifier', + type: 'keyword', + }, + 'zeek.notice.suppress_for': { + category: 'zeek', + description: + 'This field indicates the length of time that this unique notice should be suppressed. ', + name: 'zeek.notice.suppress_for', + type: 'double', + }, + 'zeek.notice.dropped': { + category: 'zeek', + description: 'Indicate if the source IP address was dropped and denied network access. ', + name: 'zeek.notice.dropped', + type: 'boolean', + }, + 'zeek.ntlm.domain': { + category: 'zeek', + description: 'Domain name given by the client. ', + name: 'zeek.ntlm.domain', + type: 'keyword', + }, + 'zeek.ntlm.hostname': { + category: 'zeek', + description: 'Hostname given by the client. ', + name: 'zeek.ntlm.hostname', + type: 'keyword', + }, + 'zeek.ntlm.success': { + category: 'zeek', + description: 'Indicate whether or not the authentication was successful. ', + name: 'zeek.ntlm.success', + type: 'boolean', + }, + 'zeek.ntlm.username': { + category: 'zeek', + description: 'Username given by the client. ', + name: 'zeek.ntlm.username', + type: 'keyword', + }, + 'zeek.ntlm.server.name.dns': { + category: 'zeek', + description: 'DNS name given by the server in a CHALLENGE. ', + name: 'zeek.ntlm.server.name.dns', + type: 'keyword', + }, + 'zeek.ntlm.server.name.netbios': { + category: 'zeek', + description: 'NetBIOS name given by the server in a CHALLENGE. ', + name: 'zeek.ntlm.server.name.netbios', + type: 'keyword', + }, + 'zeek.ntlm.server.name.tree': { + category: 'zeek', + description: 'Tree name given by the server in a CHALLENGE. ', + name: 'zeek.ntlm.server.name.tree', + type: 'keyword', + }, + 'zeek.ocsp.file_id': { + category: 'zeek', + description: 'File id of the OCSP reply. ', + name: 'zeek.ocsp.file_id', + type: 'keyword', + }, + 'zeek.ocsp.hash.algorithm': { + category: 'zeek', + description: 'Hash algorithm used to generate issuerNameHash and issuerKeyHash. ', + name: 'zeek.ocsp.hash.algorithm', + type: 'keyword', + }, + 'zeek.ocsp.hash.issuer.name': { + category: 'zeek', + description: "Hash of the issuer's distingueshed name. ", + name: 'zeek.ocsp.hash.issuer.name', + type: 'keyword', + }, + 'zeek.ocsp.hash.issuer.key': { + category: 'zeek', + description: "Hash of the issuer's public key. ", + name: 'zeek.ocsp.hash.issuer.key', + type: 'keyword', + }, + 'zeek.ocsp.serial_number': { + category: 'zeek', + description: 'Serial number of the affected certificate. ', + name: 'zeek.ocsp.serial_number', + type: 'keyword', + }, + 'zeek.ocsp.status': { + category: 'zeek', + description: 'Status of the affected certificate. ', + name: 'zeek.ocsp.status', + type: 'keyword', + }, + 'zeek.ocsp.revoke.time': { + category: 'zeek', + description: 'Time at which the certificate was revoked. ', + name: 'zeek.ocsp.revoke.time', + type: 'date', + }, + 'zeek.ocsp.revoke.reason': { + category: 'zeek', + description: 'Reason for which the certificate was revoked. ', + name: 'zeek.ocsp.revoke.reason', + type: 'keyword', + }, + 'zeek.ocsp.update.this': { + category: 'zeek', + description: 'The time at which the status being shows is known to have been correct. ', + name: 'zeek.ocsp.update.this', + type: 'date', + }, + 'zeek.ocsp.update.next': { + category: 'zeek', + description: + 'The latest time at which new information about the status of the certificate will be available. ', + name: 'zeek.ocsp.update.next', + type: 'date', + }, + 'zeek.pe.client': { + category: 'zeek', + description: "The client's version string. ", + name: 'zeek.pe.client', + type: 'keyword', + }, + 'zeek.pe.id': { + category: 'zeek', + description: 'File id of this portable executable file. ', + name: 'zeek.pe.id', + type: 'keyword', + }, + 'zeek.pe.machine': { + category: 'zeek', + description: 'The target machine that the file was compiled for. ', + name: 'zeek.pe.machine', + type: 'keyword', + }, + 'zeek.pe.compile_time': { + category: 'zeek', + description: 'The time that the file was created at. ', + name: 'zeek.pe.compile_time', + type: 'date', + }, + 'zeek.pe.os': { + category: 'zeek', + description: 'The required operating system. ', + name: 'zeek.pe.os', + type: 'keyword', + }, + 'zeek.pe.subsystem': { + category: 'zeek', + description: 'The subsystem that is required to run this file. ', + name: 'zeek.pe.subsystem', + type: 'keyword', + }, + 'zeek.pe.is_exe': { + category: 'zeek', + description: 'Is the file an executable, or just an object file? ', + name: 'zeek.pe.is_exe', + type: 'boolean', + }, + 'zeek.pe.is_64bit': { + category: 'zeek', + description: 'Is the file a 64-bit executable? ', + name: 'zeek.pe.is_64bit', + type: 'boolean', + }, + 'zeek.pe.uses_aslr': { + category: 'zeek', + description: 'Does the file support Address Space Layout Randomization? ', + name: 'zeek.pe.uses_aslr', + type: 'boolean', + }, + 'zeek.pe.uses_dep': { + category: 'zeek', + description: 'Does the file support Data Execution Prevention? ', + name: 'zeek.pe.uses_dep', + type: 'boolean', + }, + 'zeek.pe.uses_code_integrity': { + category: 'zeek', + description: 'Does the file enforce code integrity checks? ', + name: 'zeek.pe.uses_code_integrity', + type: 'boolean', + }, + 'zeek.pe.uses_seh': { + category: 'zeek', + description: 'Does the file use structured exception handing? ', + name: 'zeek.pe.uses_seh', + type: 'boolean', + }, + 'zeek.pe.has_import_table': { + category: 'zeek', + description: 'Does the file have an import table? ', + name: 'zeek.pe.has_import_table', + type: 'boolean', + }, + 'zeek.pe.has_export_table': { + category: 'zeek', + description: 'Does the file have an export table? ', + name: 'zeek.pe.has_export_table', + type: 'boolean', + }, + 'zeek.pe.has_cert_table': { + category: 'zeek', + description: 'Does the file have an attribute certificate table? ', + name: 'zeek.pe.has_cert_table', + type: 'boolean', + }, + 'zeek.pe.has_debug_data': { + category: 'zeek', + description: 'Does the file have a debug table? ', + name: 'zeek.pe.has_debug_data', + type: 'boolean', + }, + 'zeek.pe.section_names': { + category: 'zeek', + description: 'The names of the sections, in order. ', + name: 'zeek.pe.section_names', + type: 'keyword', + }, + 'zeek.radius.username': { + category: 'zeek', + description: 'The username, if present. ', + name: 'zeek.radius.username', + type: 'keyword', + }, + 'zeek.radius.mac': { + category: 'zeek', + description: 'MAC address, if present. ', + name: 'zeek.radius.mac', + type: 'keyword', + }, + 'zeek.radius.framed_addr': { + category: 'zeek', + description: + 'The address given to the network access server, if present. This is only a hint from the RADIUS server and the network access server is not required to honor the address. ', + name: 'zeek.radius.framed_addr', + type: 'ip', + }, + 'zeek.radius.remote_ip': { + category: 'zeek', + description: + 'Remote IP address, if present. This is collected from the Tunnel-Client-Endpoint attribute. ', + name: 'zeek.radius.remote_ip', + type: 'ip', + }, + 'zeek.radius.connect_info': { + category: 'zeek', + description: 'Connect info, if present. ', + name: 'zeek.radius.connect_info', + type: 'keyword', + }, + 'zeek.radius.reply_msg': { + category: 'zeek', + description: + 'Reply message from the server challenge. This is frequently shown to the user authenticating. ', + name: 'zeek.radius.reply_msg', + type: 'keyword', + }, + 'zeek.radius.result': { + category: 'zeek', + description: 'Successful or failed authentication. ', + name: 'zeek.radius.result', + type: 'keyword', + }, + 'zeek.radius.ttl': { + category: 'zeek', + description: + 'The duration between the first request and either the "Access-Accept" message or an error. If the field is empty, it means that either the request or response was not seen. ', + name: 'zeek.radius.ttl', + type: 'integer', + }, + 'zeek.radius.logged': { + category: 'zeek', + description: 'Whether this has already been logged and can be ignored. ', + name: 'zeek.radius.logged', + type: 'boolean', + }, + 'zeek.rdp.cookie': { + category: 'zeek', + description: 'Cookie value used by the client machine. This is typically a username. ', + name: 'zeek.rdp.cookie', + type: 'keyword', + }, + 'zeek.rdp.result': { + category: 'zeek', + description: + "Status result for the connection. It's a mix between RDP negotation failure messages and GCC server create response messages. ", + name: 'zeek.rdp.result', + type: 'keyword', + }, + 'zeek.rdp.security_protocol': { + category: 'zeek', + description: 'Security protocol chosen by the server. ', + name: 'zeek.rdp.security_protocol', + type: 'keyword', + }, + 'zeek.rdp.keyboard_layout': { + category: 'zeek', + description: 'Keyboard layout (language) of the client machine. ', + name: 'zeek.rdp.keyboard_layout', + type: 'keyword', + }, + 'zeek.rdp.client.build': { + category: 'zeek', + description: 'RDP client version used by the client machine. ', + name: 'zeek.rdp.client.build', + type: 'keyword', + }, + 'zeek.rdp.client.client_name': { + category: 'zeek', + description: 'Name of the client machine. ', + name: 'zeek.rdp.client.client_name', + type: 'keyword', + }, + 'zeek.rdp.client.product_id': { + category: 'zeek', + description: 'Product ID of the client machine. ', + name: 'zeek.rdp.client.product_id', + type: 'keyword', + }, + 'zeek.rdp.desktop.width': { + category: 'zeek', + description: 'Desktop width of the client machine. ', + name: 'zeek.rdp.desktop.width', + type: 'integer', + }, + 'zeek.rdp.desktop.height': { + category: 'zeek', + description: 'Desktop height of the client machine. ', + name: 'zeek.rdp.desktop.height', + type: 'integer', + }, + 'zeek.rdp.desktop.color_depth': { + category: 'zeek', + description: 'The color depth requested by the client in the high_color_depth field. ', + name: 'zeek.rdp.desktop.color_depth', + type: 'keyword', + }, + 'zeek.rdp.cert.type': { + category: 'zeek', + description: + 'If the connection is being encrypted with native RDP encryption, this is the type of cert being used. ', + name: 'zeek.rdp.cert.type', + type: 'keyword', + }, + 'zeek.rdp.cert.count': { + category: 'zeek', + description: 'The number of certs seen. X.509 can transfer an entire certificate chain. ', + name: 'zeek.rdp.cert.count', + type: 'integer', + }, + 'zeek.rdp.cert.permanent': { + category: 'zeek', + description: + 'Indicates if the provided certificate or certificate chain is permanent or temporary. ', + name: 'zeek.rdp.cert.permanent', + type: 'boolean', + }, + 'zeek.rdp.encryption.level': { + category: 'zeek', + description: 'Encryption level of the connection. ', + name: 'zeek.rdp.encryption.level', + type: 'keyword', + }, + 'zeek.rdp.encryption.method': { + category: 'zeek', + description: 'Encryption method of the connection. ', + name: 'zeek.rdp.encryption.method', + type: 'keyword', + }, + 'zeek.rdp.done': { + category: 'zeek', + description: 'Track status of logging RDP connections. ', + name: 'zeek.rdp.done', + type: 'boolean', + }, + 'zeek.rdp.ssl': { + category: 'zeek', + description: + '(present if policy/protocols/rdp/indicate_ssl.bro is loaded) Flag the connection if it was seen over SSL. ', + name: 'zeek.rdp.ssl', + type: 'boolean', + }, + 'zeek.rfb.version.client.major': { + category: 'zeek', + description: 'Major version of the client. ', + name: 'zeek.rfb.version.client.major', + type: 'keyword', + }, + 'zeek.rfb.version.client.minor': { + category: 'zeek', + description: 'Minor version of the client. ', + name: 'zeek.rfb.version.client.minor', + type: 'keyword', + }, + 'zeek.rfb.version.server.major': { + category: 'zeek', + description: 'Major version of the server. ', + name: 'zeek.rfb.version.server.major', + type: 'keyword', + }, + 'zeek.rfb.version.server.minor': { + category: 'zeek', + description: 'Minor version of the server. ', + name: 'zeek.rfb.version.server.minor', + type: 'keyword', + }, + 'zeek.rfb.auth.success': { + category: 'zeek', + description: 'Whether or not authentication was successful. ', + name: 'zeek.rfb.auth.success', + type: 'boolean', + }, + 'zeek.rfb.auth.method': { + category: 'zeek', + description: 'Identifier of authentication method used. ', + name: 'zeek.rfb.auth.method', + type: 'keyword', + }, + 'zeek.rfb.share_flag': { + category: 'zeek', + description: 'Whether the client has an exclusive or a shared session. ', + name: 'zeek.rfb.share_flag', + type: 'boolean', + }, + 'zeek.rfb.desktop_name': { + category: 'zeek', + description: 'Name of the screen that is being shared. ', + name: 'zeek.rfb.desktop_name', + type: 'keyword', + }, + 'zeek.rfb.width': { + category: 'zeek', + description: 'Width of the screen that is being shared. ', + name: 'zeek.rfb.width', + type: 'integer', + }, + 'zeek.rfb.height': { + category: 'zeek', + description: 'Height of the screen that is being shared. ', + name: 'zeek.rfb.height', + type: 'integer', + }, + 'zeek.sip.transaction_depth': { + category: 'zeek', + description: + 'Represents the pipelined depth into the connection of this request/response transaction. ', + name: 'zeek.sip.transaction_depth', + type: 'integer', + }, + 'zeek.sip.sequence.method': { + category: 'zeek', + description: 'Verb used in the SIP request (INVITE, REGISTER etc.). ', + name: 'zeek.sip.sequence.method', + type: 'keyword', + }, + 'zeek.sip.sequence.number': { + category: 'zeek', + description: 'Contents of the CSeq: header from the client. ', + name: 'zeek.sip.sequence.number', + type: 'keyword', + }, + 'zeek.sip.uri': { + category: 'zeek', + description: 'URI used in the request. ', + name: 'zeek.sip.uri', + type: 'keyword', + }, + 'zeek.sip.date': { + category: 'zeek', + description: 'Contents of the Date: header from the client. ', + name: 'zeek.sip.date', + type: 'keyword', + }, + 'zeek.sip.request.from': { + category: 'zeek', + description: + "Contents of the request From: header Note: The tag= value that's usually appended to the sender is stripped off and not logged. ", + name: 'zeek.sip.request.from', + type: 'keyword', + }, + 'zeek.sip.request.to': { + category: 'zeek', + description: 'Contents of the To: header. ', + name: 'zeek.sip.request.to', + type: 'keyword', + }, + 'zeek.sip.request.path': { + category: 'zeek', + description: 'The client message transmission path, as extracted from the headers. ', + name: 'zeek.sip.request.path', + type: 'keyword', + }, + 'zeek.sip.request.body_length': { + category: 'zeek', + description: 'Contents of the Content-Length: header from the client. ', + name: 'zeek.sip.request.body_length', + type: 'long', + }, + 'zeek.sip.response.from': { + category: 'zeek', + description: + "Contents of the response From: header Note: The tag= value that's usually appended to the sender is stripped off and not logged. ", + name: 'zeek.sip.response.from', + type: 'keyword', + }, + 'zeek.sip.response.to': { + category: 'zeek', + description: 'Contents of the response To: header. ', + name: 'zeek.sip.response.to', + type: 'keyword', + }, + 'zeek.sip.response.path': { + category: 'zeek', + description: 'The server message transmission path, as extracted from the headers. ', + name: 'zeek.sip.response.path', + type: 'keyword', + }, + 'zeek.sip.response.body_length': { + category: 'zeek', + description: 'Contents of the Content-Length: header from the server. ', + name: 'zeek.sip.response.body_length', + type: 'long', + }, + 'zeek.sip.reply_to': { + category: 'zeek', + description: 'Contents of the Reply-To: header. ', + name: 'zeek.sip.reply_to', + type: 'keyword', + }, + 'zeek.sip.call_id': { + category: 'zeek', + description: 'Contents of the Call-ID: header from the client. ', + name: 'zeek.sip.call_id', + type: 'keyword', + }, + 'zeek.sip.subject': { + category: 'zeek', + description: 'Contents of the Subject: header from the client. ', + name: 'zeek.sip.subject', + type: 'keyword', + }, + 'zeek.sip.user_agent': { + category: 'zeek', + description: 'Contents of the User-Agent: header from the client. ', + name: 'zeek.sip.user_agent', + type: 'keyword', + }, + 'zeek.sip.status.code': { + category: 'zeek', + description: 'Status code returned by the server. ', + name: 'zeek.sip.status.code', + type: 'integer', + }, + 'zeek.sip.status.msg': { + category: 'zeek', + description: 'Status message returned by the server. ', + name: 'zeek.sip.status.msg', + type: 'keyword', + }, + 'zeek.sip.warning': { + category: 'zeek', + description: 'Contents of the Warning: header. ', + name: 'zeek.sip.warning', + type: 'keyword', + }, + 'zeek.sip.content_type': { + category: 'zeek', + description: 'Contents of the Content-Type: header from the server. ', + name: 'zeek.sip.content_type', + type: 'keyword', + }, + 'zeek.smb_cmd.command': { + category: 'zeek', + description: 'The command sent by the client. ', + name: 'zeek.smb_cmd.command', + type: 'keyword', + }, + 'zeek.smb_cmd.sub_command': { + category: 'zeek', + description: 'The subcommand sent by the client, if present. ', + name: 'zeek.smb_cmd.sub_command', + type: 'keyword', + }, + 'zeek.smb_cmd.argument': { + category: 'zeek', + description: 'Command argument sent by the client, if any. ', + name: 'zeek.smb_cmd.argument', + type: 'keyword', + }, + 'zeek.smb_cmd.status': { + category: 'zeek', + description: "Server reply to the client's command. ", + name: 'zeek.smb_cmd.status', + type: 'keyword', + }, + 'zeek.smb_cmd.rtt': { + category: 'zeek', + description: 'Round trip time from the request to the response. ', + name: 'zeek.smb_cmd.rtt', + type: 'double', + }, + 'zeek.smb_cmd.version': { + category: 'zeek', + description: 'Version of SMB for the command. ', + name: 'zeek.smb_cmd.version', + type: 'keyword', + }, + 'zeek.smb_cmd.username': { + category: 'zeek', + description: 'Authenticated username, if available. ', + name: 'zeek.smb_cmd.username', + type: 'keyword', + }, + 'zeek.smb_cmd.tree': { + category: 'zeek', + description: + 'If this is related to a tree, this is the tree that was used for the current command. ', + name: 'zeek.smb_cmd.tree', + type: 'keyword', + }, + 'zeek.smb_cmd.tree_service': { + category: 'zeek', + description: 'The type of tree (disk share, printer share, named pipe, etc.). ', + name: 'zeek.smb_cmd.tree_service', + type: 'keyword', + }, + 'zeek.smb_cmd.file.name': { + category: 'zeek', + description: 'Filename if one was seen. ', + name: 'zeek.smb_cmd.file.name', + type: 'keyword', + }, + 'zeek.smb_cmd.file.action': { + category: 'zeek', + description: 'Action this log record represents. ', + name: 'zeek.smb_cmd.file.action', + type: 'keyword', + }, + 'zeek.smb_cmd.file.uid': { + category: 'zeek', + description: 'UID of the referenced file. ', + name: 'zeek.smb_cmd.file.uid', + type: 'keyword', + }, + 'zeek.smb_cmd.file.host.tx': { + category: 'zeek', + description: 'Address of the transmitting host. ', + name: 'zeek.smb_cmd.file.host.tx', + type: 'ip', + }, + 'zeek.smb_cmd.file.host.rx': { + category: 'zeek', + description: 'Address of the receiving host. ', + name: 'zeek.smb_cmd.file.host.rx', + type: 'ip', + }, + 'zeek.smb_cmd.smb1_offered_dialects': { + category: 'zeek', + description: + 'Present if base/protocols/smb/smb1-main.bro is loaded. Dialects offered by the client. ', + name: 'zeek.smb_cmd.smb1_offered_dialects', + type: 'keyword', + }, + 'zeek.smb_cmd.smb2_offered_dialects': { + category: 'zeek', + description: + 'Present if base/protocols/smb/smb2-main.bro is loaded. Dialects offered by the client. ', + name: 'zeek.smb_cmd.smb2_offered_dialects', + type: 'integer', + }, + 'zeek.smb_files.action': { + category: 'zeek', + description: 'Action this log record represents. ', + name: 'zeek.smb_files.action', + type: 'keyword', + }, + 'zeek.smb_files.fid': { + category: 'zeek', + description: 'ID referencing this file. ', + name: 'zeek.smb_files.fid', + type: 'integer', + }, + 'zeek.smb_files.name': { + category: 'zeek', + description: 'Filename if one was seen. ', + name: 'zeek.smb_files.name', + type: 'keyword', + }, + 'zeek.smb_files.path': { + category: 'zeek', + description: 'Path pulled from the tree this file was transferred to or from. ', + name: 'zeek.smb_files.path', + type: 'keyword', + }, + 'zeek.smb_files.previous_name': { + category: 'zeek', + description: "If the rename action was seen, this will be the file's previous name. ", + name: 'zeek.smb_files.previous_name', + type: 'keyword', + }, + 'zeek.smb_files.size': { + category: 'zeek', + description: 'Byte size of the file. ', + name: 'zeek.smb_files.size', + type: 'long', + }, + 'zeek.smb_files.times.accessed': { + category: 'zeek', + description: "The file's access time. ", + name: 'zeek.smb_files.times.accessed', + type: 'date', + }, + 'zeek.smb_files.times.changed': { + category: 'zeek', + description: "The file's change time. ", + name: 'zeek.smb_files.times.changed', + type: 'date', + }, + 'zeek.smb_files.times.created': { + category: 'zeek', + description: "The file's create time. ", + name: 'zeek.smb_files.times.created', + type: 'date', + }, + 'zeek.smb_files.times.modified': { + category: 'zeek', + description: "The file's modify time. ", + name: 'zeek.smb_files.times.modified', + type: 'date', + }, + 'zeek.smb_files.uuid': { + category: 'zeek', + description: 'UUID referencing this file if DCE/RPC. ', + name: 'zeek.smb_files.uuid', + type: 'keyword', + }, + 'zeek.smb_mapping.path': { + category: 'zeek', + description: 'Name of the tree path. ', + name: 'zeek.smb_mapping.path', + type: 'keyword', + }, + 'zeek.smb_mapping.service': { + category: 'zeek', + description: 'The type of resource of the tree (disk share, printer share, named pipe, etc.). ', + name: 'zeek.smb_mapping.service', + type: 'keyword', + }, + 'zeek.smb_mapping.native_file_system': { + category: 'zeek', + description: 'File system of the tree. ', + name: 'zeek.smb_mapping.native_file_system', + type: 'keyword', + }, + 'zeek.smb_mapping.share_type': { + category: 'zeek', + description: + 'If this is SMB2, a share type will be included. For SMB1, the type of share will be deduced and included as well. ', + name: 'zeek.smb_mapping.share_type', + type: 'keyword', + }, + 'zeek.smtp.transaction_depth': { + category: 'zeek', + description: + 'A count to represent the depth of this message transaction in a single connection where multiple messages were transferred. ', + name: 'zeek.smtp.transaction_depth', + type: 'integer', + }, + 'zeek.smtp.helo': { + category: 'zeek', + description: 'Contents of the Helo header. ', + name: 'zeek.smtp.helo', + type: 'keyword', + }, + 'zeek.smtp.mail_from': { + category: 'zeek', + description: 'Email addresses found in the MAIL FROM header. ', + name: 'zeek.smtp.mail_from', + type: 'keyword', + }, + 'zeek.smtp.rcpt_to': { + category: 'zeek', + description: 'Email addresses found in the RCPT TO header. ', + name: 'zeek.smtp.rcpt_to', + type: 'keyword', + }, + 'zeek.smtp.date': { + category: 'zeek', + description: 'Contents of the Date header. ', + name: 'zeek.smtp.date', + type: 'date', + }, + 'zeek.smtp.from': { + category: 'zeek', + description: 'Contents of the From header. ', + name: 'zeek.smtp.from', + type: 'keyword', + }, + 'zeek.smtp.to': { + category: 'zeek', + description: 'Contents of the To header. ', + name: 'zeek.smtp.to', + type: 'keyword', + }, + 'zeek.smtp.cc': { + category: 'zeek', + description: 'Contents of the CC header. ', + name: 'zeek.smtp.cc', + type: 'keyword', + }, + 'zeek.smtp.reply_to': { + category: 'zeek', + description: 'Contents of the ReplyTo header. ', + name: 'zeek.smtp.reply_to', + type: 'keyword', + }, + 'zeek.smtp.msg_id': { + category: 'zeek', + description: 'Contents of the MsgID header. ', + name: 'zeek.smtp.msg_id', + type: 'keyword', + }, + 'zeek.smtp.in_reply_to': { + category: 'zeek', + description: 'Contents of the In-Reply-To header. ', + name: 'zeek.smtp.in_reply_to', + type: 'keyword', + }, + 'zeek.smtp.subject': { + category: 'zeek', + description: 'Contents of the Subject header. ', + name: 'zeek.smtp.subject', + type: 'keyword', + }, + 'zeek.smtp.x_originating_ip': { + category: 'zeek', + description: 'Contents of the X-Originating-IP header. ', + name: 'zeek.smtp.x_originating_ip', + type: 'keyword', + }, + 'zeek.smtp.first_received': { + category: 'zeek', + description: 'Contents of the first Received header. ', + name: 'zeek.smtp.first_received', + type: 'keyword', + }, + 'zeek.smtp.second_received': { + category: 'zeek', + description: 'Contents of the second Received header. ', + name: 'zeek.smtp.second_received', + type: 'keyword', + }, + 'zeek.smtp.last_reply': { + category: 'zeek', + description: 'The last message that the server sent to the client. ', + name: 'zeek.smtp.last_reply', + type: 'keyword', + }, + 'zeek.smtp.path': { + category: 'zeek', + description: 'The message transmission path, as extracted from the headers. ', + name: 'zeek.smtp.path', + type: 'ip', + }, + 'zeek.smtp.user_agent': { + category: 'zeek', + description: 'Value of the User-Agent header from the client. ', + name: 'zeek.smtp.user_agent', + type: 'keyword', + }, + 'zeek.smtp.tls': { + category: 'zeek', + description: 'Indicates that the connection has switched to using TLS. ', + name: 'zeek.smtp.tls', + type: 'boolean', + }, + 'zeek.smtp.process_received_from': { + category: 'zeek', + description: 'Indicates if the "Received: from" headers should still be processed. ', + name: 'zeek.smtp.process_received_from', + type: 'boolean', + }, + 'zeek.smtp.has_client_activity': { + category: 'zeek', + description: 'Indicates if client activity has been seen, but not yet logged. ', + name: 'zeek.smtp.has_client_activity', + type: 'boolean', + }, + 'zeek.smtp.fuids': { + category: 'zeek', + description: + '(present if base/protocols/smtp/files.bro is loaded) An ordered vector of file unique IDs seen attached to the message. ', + name: 'zeek.smtp.fuids', + type: 'keyword', + }, + 'zeek.smtp.is_webmail': { + category: 'zeek', + description: 'Indicates if the message was sent through a webmail interface. ', + name: 'zeek.smtp.is_webmail', + type: 'boolean', + }, + 'zeek.snmp.duration': { + category: 'zeek', + description: + 'The amount of time between the first packet beloning to the SNMP session and the latest one seen. ', + name: 'zeek.snmp.duration', + type: 'double', + }, + 'zeek.snmp.version': { + category: 'zeek', + description: 'The version of SNMP being used. ', + name: 'zeek.snmp.version', + type: 'keyword', + }, + 'zeek.snmp.community': { + category: 'zeek', + description: + "The community string of the first SNMP packet associated with the session. This is used as part of SNMP's (v1 and v2c) administrative/security framework. See RFC 1157 or RFC 1901. ", + name: 'zeek.snmp.community', + type: 'keyword', + }, + 'zeek.snmp.get.requests': { + category: 'zeek', + description: + 'The number of variable bindings in GetRequest/GetNextRequest PDUs seen for the session. ', + name: 'zeek.snmp.get.requests', + type: 'integer', + }, + 'zeek.snmp.get.bulk_requests': { + category: 'zeek', + description: 'The number of variable bindings in GetBulkRequest PDUs seen for the session. ', + name: 'zeek.snmp.get.bulk_requests', + type: 'integer', + }, + 'zeek.snmp.get.responses': { + category: 'zeek', + description: + 'The number of variable bindings in GetResponse/Response PDUs seen for the session. ', + name: 'zeek.snmp.get.responses', + type: 'integer', + }, + 'zeek.snmp.set.requests': { + category: 'zeek', + description: 'The number of variable bindings in SetRequest PDUs seen for the session. ', + name: 'zeek.snmp.set.requests', + type: 'integer', + }, + 'zeek.snmp.display_string': { + category: 'zeek', + description: 'A system description of the SNMP responder endpoint. ', + name: 'zeek.snmp.display_string', + type: 'keyword', + }, + 'zeek.snmp.up_since': { + category: 'zeek', + description: "The time at which the SNMP responder endpoint claims it's been up since. ", + name: 'zeek.snmp.up_since', + type: 'date', + }, + 'zeek.socks.version': { + category: 'zeek', + description: 'Protocol version of SOCKS. ', + name: 'zeek.socks.version', + type: 'integer', + }, + 'zeek.socks.user': { + category: 'zeek', + description: 'Username used to request a login to the proxy. ', + name: 'zeek.socks.user', + type: 'keyword', + }, + 'zeek.socks.password': { + category: 'zeek', + description: 'Password used to request a login to the proxy. ', + name: 'zeek.socks.password', + type: 'keyword', + }, + 'zeek.socks.status': { + category: 'zeek', + description: 'Server status for the attempt at using the proxy. ', + name: 'zeek.socks.status', + type: 'keyword', + }, + 'zeek.socks.request.host': { + category: 'zeek', + description: 'Client requested SOCKS address. Could be an address, a name or both. ', + name: 'zeek.socks.request.host', + type: 'keyword', + }, + 'zeek.socks.request.port': { + category: 'zeek', + description: 'Client requested port. ', + name: 'zeek.socks.request.port', + type: 'integer', + }, + 'zeek.socks.bound.host': { + category: 'zeek', + description: 'Server bound address. Could be an address, a name or both. ', + name: 'zeek.socks.bound.host', + type: 'keyword', + }, + 'zeek.socks.bound.port': { + category: 'zeek', + description: 'Server bound port. ', + name: 'zeek.socks.bound.port', + type: 'integer', + }, + 'zeek.socks.capture_password': { + category: 'zeek', + description: 'Determines if the password will be captured for this request. ', + name: 'zeek.socks.capture_password', + type: 'boolean', + }, + 'zeek.ssh.client': { + category: 'zeek', + description: "The client's version string. ", + name: 'zeek.ssh.client', + type: 'keyword', + }, + 'zeek.ssh.direction': { + category: 'zeek', + description: + 'Direction of the connection. If the client was a local host logging into an external host, this would be OUTBOUND. INBOUND would be set for the opposite situation. ', + name: 'zeek.ssh.direction', + type: 'keyword', + }, + 'zeek.ssh.host_key': { + category: 'zeek', + description: "The server's key thumbprint. ", + name: 'zeek.ssh.host_key', + type: 'keyword', + }, + 'zeek.ssh.server': { + category: 'zeek', + description: "The server's version string. ", + name: 'zeek.ssh.server', + type: 'keyword', + }, + 'zeek.ssh.version': { + category: 'zeek', + description: 'SSH major version (1 or 2). ', + name: 'zeek.ssh.version', + type: 'integer', + }, + 'zeek.ssh.algorithm.cipher': { + category: 'zeek', + description: 'The encryption algorithm in use. ', + name: 'zeek.ssh.algorithm.cipher', + type: 'keyword', + }, + 'zeek.ssh.algorithm.compression': { + category: 'zeek', + description: 'The compression algorithm in use. ', + name: 'zeek.ssh.algorithm.compression', + type: 'keyword', + }, + 'zeek.ssh.algorithm.host_key': { + category: 'zeek', + description: "The server host key's algorithm. ", + name: 'zeek.ssh.algorithm.host_key', + type: 'keyword', + }, + 'zeek.ssh.algorithm.key_exchange': { + category: 'zeek', + description: 'The key exchange algorithm in use. ', + name: 'zeek.ssh.algorithm.key_exchange', + type: 'keyword', + }, + 'zeek.ssh.algorithm.mac': { + category: 'zeek', + description: 'The signing (MAC) algorithm in use. ', + name: 'zeek.ssh.algorithm.mac', + type: 'keyword', + }, + 'zeek.ssh.auth.attempts': { + category: 'zeek', + description: + "The number of authentication attemps we observed. There's always at least one, since some servers might support no authentication at all. It's important to note that not all of these are failures, since some servers require two-factor auth (e.g. password AND pubkey). ", + name: 'zeek.ssh.auth.attempts', + type: 'integer', + }, + 'zeek.ssh.auth.success': { + category: 'zeek', + description: 'Authentication result. ', + name: 'zeek.ssh.auth.success', + type: 'boolean', + }, + 'zeek.ssl.version': { + category: 'zeek', + description: 'SSL/TLS version that was logged. ', + name: 'zeek.ssl.version', + type: 'keyword', + }, + 'zeek.ssl.cipher': { + category: 'zeek', + description: 'SSL/TLS cipher suite that was logged. ', + name: 'zeek.ssl.cipher', + type: 'keyword', + }, + 'zeek.ssl.curve': { + category: 'zeek', + description: 'Elliptic curve that was logged when using ECDH/ECDHE. ', + name: 'zeek.ssl.curve', + type: 'keyword', + }, + 'zeek.ssl.resumed': { + category: 'zeek', + description: + 'Flag to indicate if the session was resumed reusing the key material exchanged in an earlier connection. ', + name: 'zeek.ssl.resumed', + type: 'boolean', + }, + 'zeek.ssl.next_protocol': { + category: 'zeek', + description: + 'Next protocol the server chose using the application layer next protocol extension. ', + name: 'zeek.ssl.next_protocol', + type: 'keyword', + }, + 'zeek.ssl.established': { + category: 'zeek', + description: 'Flag to indicate if this ssl session has been established successfully. ', + name: 'zeek.ssl.established', + type: 'boolean', + }, + 'zeek.ssl.validation.status': { + category: 'zeek', + description: 'Result of certificate validation for this connection. ', + name: 'zeek.ssl.validation.status', + type: 'keyword', + }, + 'zeek.ssl.validation.code': { + category: 'zeek', + description: + 'Result of certificate validation for this connection, given as OpenSSL validation code. ', + name: 'zeek.ssl.validation.code', + type: 'keyword', + }, + 'zeek.ssl.last_alert': { + category: 'zeek', + description: 'Last alert that was seen during the connection. ', + name: 'zeek.ssl.last_alert', + type: 'keyword', + }, + 'zeek.ssl.server.name': { + category: 'zeek', + description: + 'Value of the Server Name Indicator SSL/TLS extension. It indicates the server name that the client was requesting. ', + name: 'zeek.ssl.server.name', + type: 'keyword', + }, + 'zeek.ssl.server.cert_chain': { + category: 'zeek', + description: + 'Chain of certificates offered by the server to validate its complete signing chain. ', + name: 'zeek.ssl.server.cert_chain', + type: 'keyword', + }, + 'zeek.ssl.server.cert_chain_fuids': { + category: 'zeek', + description: + 'An ordered vector of certificate file identifiers for the certificates offered by the server. ', + name: 'zeek.ssl.server.cert_chain_fuids', + type: 'keyword', + }, + 'zeek.ssl.server.issuer.common_name': { + category: 'zeek', + description: 'Common name of the signer of the X.509 certificate offered by the server. ', + name: 'zeek.ssl.server.issuer.common_name', + type: 'keyword', + }, + 'zeek.ssl.server.issuer.country': { + category: 'zeek', + description: 'Country code of the signer of the X.509 certificate offered by the server. ', + name: 'zeek.ssl.server.issuer.country', + type: 'keyword', + }, + 'zeek.ssl.server.issuer.locality': { + category: 'zeek', + description: 'Locality of the signer of the X.509 certificate offered by the server. ', + name: 'zeek.ssl.server.issuer.locality', + type: 'keyword', + }, + 'zeek.ssl.server.issuer.organization': { + category: 'zeek', + description: 'Organization of the signer of the X.509 certificate offered by the server. ', + name: 'zeek.ssl.server.issuer.organization', + type: 'keyword', + }, + 'zeek.ssl.server.issuer.organizational_unit': { + category: 'zeek', + description: + 'Organizational unit of the signer of the X.509 certificate offered by the server. ', + name: 'zeek.ssl.server.issuer.organizational_unit', + type: 'keyword', + }, + 'zeek.ssl.server.issuer.state': { + category: 'zeek', + description: + 'State or province name of the signer of the X.509 certificate offered by the server. ', + name: 'zeek.ssl.server.issuer.state', + type: 'keyword', + }, + 'zeek.ssl.server.subject.common_name': { + category: 'zeek', + description: 'Common name of the X.509 certificate offered by the server. ', + name: 'zeek.ssl.server.subject.common_name', + type: 'keyword', + }, + 'zeek.ssl.server.subject.country': { + category: 'zeek', + description: 'Country code of the X.509 certificate offered by the server. ', + name: 'zeek.ssl.server.subject.country', + type: 'keyword', + }, + 'zeek.ssl.server.subject.locality': { + category: 'zeek', + description: 'Locality of the X.509 certificate offered by the server. ', + name: 'zeek.ssl.server.subject.locality', + type: 'keyword', + }, + 'zeek.ssl.server.subject.organization': { + category: 'zeek', + description: 'Organization of the X.509 certificate offered by the server. ', + name: 'zeek.ssl.server.subject.organization', + type: 'keyword', + }, + 'zeek.ssl.server.subject.organizational_unit': { + category: 'zeek', + description: 'Organizational unit of the X.509 certificate offered by the server. ', + name: 'zeek.ssl.server.subject.organizational_unit', + type: 'keyword', + }, + 'zeek.ssl.server.subject.state': { + category: 'zeek', + description: 'State or province name of the X.509 certificate offered by the server. ', + name: 'zeek.ssl.server.subject.state', + type: 'keyword', + }, + 'zeek.ssl.client.cert_chain': { + category: 'zeek', + description: + 'Chain of certificates offered by the client to validate its complete signing chain. ', + name: 'zeek.ssl.client.cert_chain', + type: 'keyword', + }, + 'zeek.ssl.client.cert_chain_fuids': { + category: 'zeek', + description: + 'An ordered vector of certificate file identifiers for the certificates offered by the client. ', + name: 'zeek.ssl.client.cert_chain_fuids', + type: 'keyword', + }, + 'zeek.ssl.client.issuer.common_name': { + category: 'zeek', + description: 'Common name of the signer of the X.509 certificate offered by the client. ', + name: 'zeek.ssl.client.issuer.common_name', + type: 'keyword', + }, + 'zeek.ssl.client.issuer.country': { + category: 'zeek', + description: 'Country code of the signer of the X.509 certificate offered by the client. ', + name: 'zeek.ssl.client.issuer.country', + type: 'keyword', + }, + 'zeek.ssl.client.issuer.locality': { + category: 'zeek', + description: 'Locality of the signer of the X.509 certificate offered by the client. ', + name: 'zeek.ssl.client.issuer.locality', + type: 'keyword', + }, + 'zeek.ssl.client.issuer.organization': { + category: 'zeek', + description: 'Organization of the signer of the X.509 certificate offered by the client. ', + name: 'zeek.ssl.client.issuer.organization', + type: 'keyword', + }, + 'zeek.ssl.client.issuer.organizational_unit': { + category: 'zeek', + description: + 'Organizational unit of the signer of the X.509 certificate offered by the client. ', + name: 'zeek.ssl.client.issuer.organizational_unit', + type: 'keyword', + }, + 'zeek.ssl.client.issuer.state': { + category: 'zeek', + description: + 'State or province name of the signer of the X.509 certificate offered by the client. ', + name: 'zeek.ssl.client.issuer.state', + type: 'keyword', + }, + 'zeek.ssl.client.subject.common_name': { + category: 'zeek', + description: 'Common name of the X.509 certificate offered by the client. ', + name: 'zeek.ssl.client.subject.common_name', + type: 'keyword', + }, + 'zeek.ssl.client.subject.country': { + category: 'zeek', + description: 'Country code of the X.509 certificate offered by the client. ', + name: 'zeek.ssl.client.subject.country', + type: 'keyword', + }, + 'zeek.ssl.client.subject.locality': { + category: 'zeek', + description: 'Locality of the X.509 certificate offered by the client. ', + name: 'zeek.ssl.client.subject.locality', + type: 'keyword', + }, + 'zeek.ssl.client.subject.organization': { + category: 'zeek', + description: 'Organization of the X.509 certificate offered by the client. ', + name: 'zeek.ssl.client.subject.organization', + type: 'keyword', + }, + 'zeek.ssl.client.subject.organizational_unit': { + category: 'zeek', + description: 'Organizational unit of the X.509 certificate offered by the client. ', + name: 'zeek.ssl.client.subject.organizational_unit', + type: 'keyword', + }, + 'zeek.ssl.client.subject.state': { + category: 'zeek', + description: 'State or province name of the X.509 certificate offered by the client. ', + name: 'zeek.ssl.client.subject.state', + type: 'keyword', + }, + 'zeek.stats.peer': { + category: 'zeek', + description: 'Peer that generated this log. Mostly for clusters. ', + name: 'zeek.stats.peer', + type: 'keyword', + }, + 'zeek.stats.memory': { + category: 'zeek', + description: 'Amount of memory currently in use in MB. ', + name: 'zeek.stats.memory', + type: 'integer', + }, + 'zeek.stats.packets.processed': { + category: 'zeek', + description: 'Number of packets processed since the last stats interval. ', + name: 'zeek.stats.packets.processed', + type: 'long', + }, + 'zeek.stats.packets.dropped': { + category: 'zeek', + description: + 'Number of packets dropped since the last stats interval if reading live traffic. ', + name: 'zeek.stats.packets.dropped', + type: 'long', + }, + 'zeek.stats.packets.received': { + category: 'zeek', + description: + 'Number of packets seen on the link since the last stats interval if reading live traffic. ', + name: 'zeek.stats.packets.received', + type: 'long', + }, + 'zeek.stats.bytes.received': { + category: 'zeek', + description: 'Number of bytes received since the last stats interval if reading live traffic. ', + name: 'zeek.stats.bytes.received', + type: 'long', + }, + 'zeek.stats.connections.tcp.active': { + category: 'zeek', + description: 'TCP connections currently in memory. ', + name: 'zeek.stats.connections.tcp.active', + type: 'integer', + }, + 'zeek.stats.connections.tcp.count': { + category: 'zeek', + description: 'TCP connections seen since last stats interval. ', + name: 'zeek.stats.connections.tcp.count', + type: 'integer', + }, + 'zeek.stats.connections.udp.active': { + category: 'zeek', + description: 'UDP connections currently in memory. ', + name: 'zeek.stats.connections.udp.active', + type: 'integer', + }, + 'zeek.stats.connections.udp.count': { + category: 'zeek', + description: 'UDP connections seen since last stats interval. ', + name: 'zeek.stats.connections.udp.count', + type: 'integer', + }, + 'zeek.stats.connections.icmp.active': { + category: 'zeek', + description: 'ICMP connections currently in memory. ', + name: 'zeek.stats.connections.icmp.active', + type: 'integer', + }, + 'zeek.stats.connections.icmp.count': { + category: 'zeek', + description: 'ICMP connections seen since last stats interval. ', + name: 'zeek.stats.connections.icmp.count', + type: 'integer', + }, + 'zeek.stats.events.processed': { + category: 'zeek', + description: 'Number of events processed since the last stats interval. ', + name: 'zeek.stats.events.processed', + type: 'integer', + }, + 'zeek.stats.events.queued': { + category: 'zeek', + description: 'Number of events that have been queued since the last stats interval. ', + name: 'zeek.stats.events.queued', + type: 'integer', + }, + 'zeek.stats.timers.count': { + category: 'zeek', + description: 'Number of timers scheduled since last stats interval. ', + name: 'zeek.stats.timers.count', + type: 'integer', + }, + 'zeek.stats.timers.active': { + category: 'zeek', + description: 'Current number of scheduled timers. ', + name: 'zeek.stats.timers.active', + type: 'integer', + }, + 'zeek.stats.files.count': { + category: 'zeek', + description: 'Number of files seen since last stats interval. ', + name: 'zeek.stats.files.count', + type: 'integer', + }, + 'zeek.stats.files.active': { + category: 'zeek', + description: 'Current number of files actively being seen. ', + name: 'zeek.stats.files.active', + type: 'integer', + }, + 'zeek.stats.dns_requests.count': { + category: 'zeek', + description: 'Number of DNS requests seen since last stats interval. ', + name: 'zeek.stats.dns_requests.count', + type: 'integer', + }, + 'zeek.stats.dns_requests.active': { + category: 'zeek', + description: 'Current number of DNS requests awaiting a reply. ', + name: 'zeek.stats.dns_requests.active', + type: 'integer', + }, + 'zeek.stats.reassembly_size.tcp': { + category: 'zeek', + description: 'Current size of TCP data in reassembly. ', + name: 'zeek.stats.reassembly_size.tcp', + type: 'integer', + }, + 'zeek.stats.reassembly_size.file': { + category: 'zeek', + description: 'Current size of File data in reassembly. ', + name: 'zeek.stats.reassembly_size.file', + type: 'integer', + }, + 'zeek.stats.reassembly_size.frag': { + category: 'zeek', + description: 'Current size of packet fragment data in reassembly. ', + name: 'zeek.stats.reassembly_size.frag', + type: 'integer', + }, + 'zeek.stats.reassembly_size.unknown': { + category: 'zeek', + description: 'Current size of unknown data in reassembly (this is only PIA buffer right now). ', + name: 'zeek.stats.reassembly_size.unknown', + type: 'integer', + }, + 'zeek.stats.timestamp_lag': { + category: 'zeek', + description: 'Lag between the wall clock and packet timestamps if reading live traffic. ', + name: 'zeek.stats.timestamp_lag', + type: 'integer', + }, + 'zeek.syslog.facility': { + category: 'zeek', + description: 'Syslog facility for the message. ', + name: 'zeek.syslog.facility', + type: 'keyword', + }, + 'zeek.syslog.severity': { + category: 'zeek', + description: 'Syslog severity for the message. ', + name: 'zeek.syslog.severity', + type: 'keyword', + }, + 'zeek.syslog.message': { + category: 'zeek', + description: 'The plain text message. ', + name: 'zeek.syslog.message', + type: 'keyword', + }, + 'zeek.tunnel.type': { + category: 'zeek', + description: 'The type of tunnel. ', + name: 'zeek.tunnel.type', + type: 'keyword', + }, + 'zeek.tunnel.action': { + category: 'zeek', + description: 'The type of activity that occurred. ', + name: 'zeek.tunnel.action', + type: 'keyword', + }, + 'zeek.weird.name': { + category: 'zeek', + description: 'The name of the weird that occurred. ', + name: 'zeek.weird.name', + type: 'keyword', + }, + 'zeek.weird.additional_info': { + category: 'zeek', + description: 'Additional information accompanying the weird if any. ', + name: 'zeek.weird.additional_info', + type: 'keyword', + }, + 'zeek.weird.notice': { + category: 'zeek', + description: 'Indicate if this weird was also turned into a notice. ', + name: 'zeek.weird.notice', + type: 'boolean', + }, + 'zeek.weird.peer': { + category: 'zeek', + description: + 'The peer that originated this weird. This is helpful in cluster deployments if a particular cluster node is having trouble to help identify which node is having trouble. ', + name: 'zeek.weird.peer', + type: 'keyword', + }, + 'zeek.weird.identifier': { + category: 'zeek', + description: + 'This field is to be provided when a weird is generated for the purpose of deduplicating weirds. The identifier string should be unique for a single instance of the weird. This field is used to define when a weird is conceptually a duplicate of a previous weird. ', + name: 'zeek.weird.identifier', + type: 'keyword', + }, + 'zeek.x509.id': { + category: 'zeek', + description: 'File id of this certificate. ', + name: 'zeek.x509.id', + type: 'keyword', + }, + 'zeek.x509.certificate.version': { + category: 'zeek', + description: 'Version number. ', + name: 'zeek.x509.certificate.version', + type: 'integer', + }, + 'zeek.x509.certificate.serial': { + category: 'zeek', + description: 'Serial number. ', + name: 'zeek.x509.certificate.serial', + type: 'keyword', + }, + 'zeek.x509.certificate.subject.country': { + category: 'zeek', + description: 'Country provided in the certificate subject. ', + name: 'zeek.x509.certificate.subject.country', + type: 'keyword', + }, + 'zeek.x509.certificate.subject.common_name': { + category: 'zeek', + description: 'Common name provided in the certificate subject. ', + name: 'zeek.x509.certificate.subject.common_name', + type: 'keyword', + }, + 'zeek.x509.certificate.subject.locality': { + category: 'zeek', + description: 'Locality provided in the certificate subject. ', + name: 'zeek.x509.certificate.subject.locality', + type: 'keyword', + }, + 'zeek.x509.certificate.subject.organization': { + category: 'zeek', + description: 'Organization provided in the certificate subject. ', + name: 'zeek.x509.certificate.subject.organization', + type: 'keyword', + }, + 'zeek.x509.certificate.subject.organizational_unit': { + category: 'zeek', + description: 'Organizational unit provided in the certificate subject. ', + name: 'zeek.x509.certificate.subject.organizational_unit', + type: 'keyword', + }, + 'zeek.x509.certificate.subject.state': { + category: 'zeek', + description: 'State or province provided in the certificate subject. ', + name: 'zeek.x509.certificate.subject.state', + type: 'keyword', + }, + 'zeek.x509.certificate.issuer.country': { + category: 'zeek', + description: 'Country provided in the certificate issuer field. ', + name: 'zeek.x509.certificate.issuer.country', + type: 'keyword', + }, + 'zeek.x509.certificate.issuer.common_name': { + category: 'zeek', + description: 'Common name provided in the certificate issuer field. ', + name: 'zeek.x509.certificate.issuer.common_name', + type: 'keyword', + }, + 'zeek.x509.certificate.issuer.locality': { + category: 'zeek', + description: 'Locality provided in the certificate issuer field. ', + name: 'zeek.x509.certificate.issuer.locality', + type: 'keyword', + }, + 'zeek.x509.certificate.issuer.organization': { + category: 'zeek', + description: 'Organization provided in the certificate issuer field. ', + name: 'zeek.x509.certificate.issuer.organization', + type: 'keyword', + }, + 'zeek.x509.certificate.issuer.organizational_unit': { + category: 'zeek', + description: 'Organizational unit provided in the certificate issuer field. ', + name: 'zeek.x509.certificate.issuer.organizational_unit', + type: 'keyword', + }, + 'zeek.x509.certificate.issuer.state': { + category: 'zeek', + description: 'State or province provided in the certificate issuer field. ', + name: 'zeek.x509.certificate.issuer.state', + type: 'keyword', + }, + 'zeek.x509.certificate.common_name': { + category: 'zeek', + description: 'Last (most specific) common name. ', + name: 'zeek.x509.certificate.common_name', + type: 'keyword', + }, + 'zeek.x509.certificate.valid.from': { + category: 'zeek', + description: 'Timestamp before when certificate is not valid. ', + name: 'zeek.x509.certificate.valid.from', + type: 'date', + }, + 'zeek.x509.certificate.valid.until': { + category: 'zeek', + description: 'Timestamp after when certificate is not valid. ', + name: 'zeek.x509.certificate.valid.until', + type: 'date', + }, + 'zeek.x509.certificate.key.algorithm': { + category: 'zeek', + description: 'Name of the key algorithm. ', + name: 'zeek.x509.certificate.key.algorithm', + type: 'keyword', + }, + 'zeek.x509.certificate.key.type': { + category: 'zeek', + description: 'Key type, if key parseable by openssl (either rsa, dsa or ec). ', + name: 'zeek.x509.certificate.key.type', + type: 'keyword', + }, + 'zeek.x509.certificate.key.length': { + category: 'zeek', + description: 'Key length in bits. ', + name: 'zeek.x509.certificate.key.length', + type: 'integer', + }, + 'zeek.x509.certificate.signature_algorithm': { + category: 'zeek', + description: 'Name of the signature algorithm. ', + name: 'zeek.x509.certificate.signature_algorithm', + type: 'keyword', + }, + 'zeek.x509.certificate.exponent': { + category: 'zeek', + description: 'Exponent, if RSA-certificate. ', + name: 'zeek.x509.certificate.exponent', + type: 'keyword', + }, + 'zeek.x509.certificate.curve': { + category: 'zeek', + description: 'Curve, if EC-certificate. ', + name: 'zeek.x509.certificate.curve', + type: 'keyword', + }, + 'zeek.x509.san.dns': { + category: 'zeek', + description: 'List of DNS entries in SAN. ', + name: 'zeek.x509.san.dns', + type: 'keyword', + }, + 'zeek.x509.san.uri': { + category: 'zeek', + description: 'List of URI entries in SAN. ', + name: 'zeek.x509.san.uri', + type: 'keyword', + }, + 'zeek.x509.san.email': { + category: 'zeek', + description: 'List of email entries in SAN. ', + name: 'zeek.x509.san.email', + type: 'keyword', + }, + 'zeek.x509.san.ip': { + category: 'zeek', + description: 'List of IP entries in SAN. ', + name: 'zeek.x509.san.ip', + type: 'ip', + }, + 'zeek.x509.san.other_fields': { + category: 'zeek', + description: 'True if the certificate contained other, not recognized or parsed name fields. ', + name: 'zeek.x509.san.other_fields', + type: 'boolean', + }, + 'zeek.x509.basic_constraints.certificate_authority': { + category: 'zeek', + description: 'CA flag set or not. ', + name: 'zeek.x509.basic_constraints.certificate_authority', + type: 'boolean', + }, + 'zeek.x509.basic_constraints.path_length': { + category: 'zeek', + description: 'Maximum path length. ', + name: 'zeek.x509.basic_constraints.path_length', + type: 'integer', + }, + 'zeek.x509.log_cert': { + category: 'zeek', + description: + 'Present if policy/protocols/ssl/log-hostcerts-only.bro is loaded Logging of certificate is suppressed if set to F. ', + name: 'zeek.x509.log_cert', + type: 'boolean', + }, + 'awscloudwatch.log_group': { + category: 'awscloudwatch', + description: 'The name of the log group to which this event belongs.', + name: 'awscloudwatch.log_group', + type: 'keyword', + }, + 'awscloudwatch.log_stream': { + category: 'awscloudwatch', + description: 'The name of the log stream to which this event belongs.', + name: 'awscloudwatch.log_stream', + type: 'keyword', + }, + 'awscloudwatch.ingestion_time': { + category: 'awscloudwatch', + description: 'The time the event was ingested in AWS CloudWatch.', + name: 'awscloudwatch.ingestion_time', + type: 'keyword', + }, + 'netflow.type': { + category: 'netflow', + description: 'The type of NetFlow record described by this event. ', + name: 'netflow.type', + type: 'keyword', + }, + 'netflow.exporter.address': { + category: 'netflow', + description: "Exporter's network address in IP:port format. ", + name: 'netflow.exporter.address', + type: 'keyword', + }, + 'netflow.exporter.source_id': { + category: 'netflow', + description: 'Observation domain ID to which this record belongs. ', + name: 'netflow.exporter.source_id', + type: 'long', + }, + 'netflow.exporter.timestamp': { + category: 'netflow', + description: 'Time and date of export. ', + name: 'netflow.exporter.timestamp', + type: 'date', + }, + 'netflow.exporter.uptime_millis': { + category: 'netflow', + description: 'How long the exporter process has been running, in milliseconds. ', + name: 'netflow.exporter.uptime_millis', + type: 'long', + }, + 'netflow.exporter.version': { + category: 'netflow', + description: 'NetFlow version used. ', + name: 'netflow.exporter.version', + type: 'integer', + }, + 'netflow.octet_delta_count': { + category: 'netflow', + name: 'netflow.octet_delta_count', + type: 'long', + }, + 'netflow.packet_delta_count': { + category: 'netflow', + name: 'netflow.packet_delta_count', + type: 'long', + }, + 'netflow.delta_flow_count': { + category: 'netflow', + name: 'netflow.delta_flow_count', + type: 'long', + }, + 'netflow.protocol_identifier': { + category: 'netflow', + name: 'netflow.protocol_identifier', + type: 'short', + }, + 'netflow.ip_class_of_service': { + category: 'netflow', + name: 'netflow.ip_class_of_service', + type: 'short', + }, + 'netflow.tcp_control_bits': { + category: 'netflow', + name: 'netflow.tcp_control_bits', + type: 'integer', + }, + 'netflow.source_transport_port': { + category: 'netflow', + name: 'netflow.source_transport_port', + type: 'integer', + }, + 'netflow.source_ipv4_address': { + category: 'netflow', + name: 'netflow.source_ipv4_address', + type: 'ip', + }, + 'netflow.source_ipv4_prefix_length': { + category: 'netflow', + name: 'netflow.source_ipv4_prefix_length', + type: 'short', + }, + 'netflow.ingress_interface': { + category: 'netflow', + name: 'netflow.ingress_interface', + type: 'long', + }, + 'netflow.destination_transport_port': { + category: 'netflow', + name: 'netflow.destination_transport_port', + type: 'integer', + }, + 'netflow.destination_ipv4_address': { + category: 'netflow', + name: 'netflow.destination_ipv4_address', + type: 'ip', + }, + 'netflow.destination_ipv4_prefix_length': { + category: 'netflow', + name: 'netflow.destination_ipv4_prefix_length', + type: 'short', + }, + 'netflow.egress_interface': { + category: 'netflow', + name: 'netflow.egress_interface', + type: 'long', + }, + 'netflow.ip_next_hop_ipv4_address': { + category: 'netflow', + name: 'netflow.ip_next_hop_ipv4_address', + type: 'ip', + }, + 'netflow.bgp_source_as_number': { + category: 'netflow', + name: 'netflow.bgp_source_as_number', + type: 'long', + }, + 'netflow.bgp_destination_as_number': { + category: 'netflow', + name: 'netflow.bgp_destination_as_number', + type: 'long', + }, + 'netflow.bgp_next_hop_ipv4_address': { + category: 'netflow', + name: 'netflow.bgp_next_hop_ipv4_address', + type: 'ip', + }, + 'netflow.post_mcast_packet_delta_count': { + category: 'netflow', + name: 'netflow.post_mcast_packet_delta_count', + type: 'long', + }, + 'netflow.post_mcast_octet_delta_count': { + category: 'netflow', + name: 'netflow.post_mcast_octet_delta_count', + type: 'long', + }, + 'netflow.flow_end_sys_up_time': { + category: 'netflow', + name: 'netflow.flow_end_sys_up_time', + type: 'long', + }, + 'netflow.flow_start_sys_up_time': { + category: 'netflow', + name: 'netflow.flow_start_sys_up_time', + type: 'long', + }, + 'netflow.post_octet_delta_count': { + category: 'netflow', + name: 'netflow.post_octet_delta_count', + type: 'long', + }, + 'netflow.post_packet_delta_count': { + category: 'netflow', + name: 'netflow.post_packet_delta_count', + type: 'long', + }, + 'netflow.minimum_ip_total_length': { + category: 'netflow', + name: 'netflow.minimum_ip_total_length', + type: 'long', + }, + 'netflow.maximum_ip_total_length': { + category: 'netflow', + name: 'netflow.maximum_ip_total_length', + type: 'long', + }, + 'netflow.source_ipv6_address': { + category: 'netflow', + name: 'netflow.source_ipv6_address', + type: 'ip', + }, + 'netflow.destination_ipv6_address': { + category: 'netflow', + name: 'netflow.destination_ipv6_address', + type: 'ip', + }, + 'netflow.source_ipv6_prefix_length': { + category: 'netflow', + name: 'netflow.source_ipv6_prefix_length', + type: 'short', + }, + 'netflow.destination_ipv6_prefix_length': { + category: 'netflow', + name: 'netflow.destination_ipv6_prefix_length', + type: 'short', + }, + 'netflow.flow_label_ipv6': { + category: 'netflow', + name: 'netflow.flow_label_ipv6', + type: 'long', + }, + 'netflow.icmp_type_code_ipv4': { + category: 'netflow', + name: 'netflow.icmp_type_code_ipv4', + type: 'integer', + }, + 'netflow.igmp_type': { + category: 'netflow', + name: 'netflow.igmp_type', + type: 'short', + }, + 'netflow.sampling_interval': { + category: 'netflow', + name: 'netflow.sampling_interval', + type: 'long', + }, + 'netflow.sampling_algorithm': { + category: 'netflow', + name: 'netflow.sampling_algorithm', + type: 'short', + }, + 'netflow.flow_active_timeout': { + category: 'netflow', + name: 'netflow.flow_active_timeout', + type: 'integer', + }, + 'netflow.flow_idle_timeout': { + category: 'netflow', + name: 'netflow.flow_idle_timeout', + type: 'integer', + }, + 'netflow.engine_type': { + category: 'netflow', + name: 'netflow.engine_type', + type: 'short', + }, + 'netflow.engine_id': { + category: 'netflow', + name: 'netflow.engine_id', + type: 'short', + }, + 'netflow.exported_octet_total_count': { + category: 'netflow', + name: 'netflow.exported_octet_total_count', + type: 'long', + }, + 'netflow.exported_message_total_count': { + category: 'netflow', + name: 'netflow.exported_message_total_count', + type: 'long', + }, + 'netflow.exported_flow_record_total_count': { + category: 'netflow', + name: 'netflow.exported_flow_record_total_count', + type: 'long', + }, + 'netflow.ipv4_router_sc': { + category: 'netflow', + name: 'netflow.ipv4_router_sc', + type: 'ip', + }, + 'netflow.source_ipv4_prefix': { + category: 'netflow', + name: 'netflow.source_ipv4_prefix', + type: 'ip', + }, + 'netflow.destination_ipv4_prefix': { + category: 'netflow', + name: 'netflow.destination_ipv4_prefix', + type: 'ip', + }, + 'netflow.mpls_top_label_type': { + category: 'netflow', + name: 'netflow.mpls_top_label_type', + type: 'short', + }, + 'netflow.mpls_top_label_ipv4_address': { + category: 'netflow', + name: 'netflow.mpls_top_label_ipv4_address', + type: 'ip', + }, + 'netflow.sampler_id': { + category: 'netflow', + name: 'netflow.sampler_id', + type: 'short', + }, + 'netflow.sampler_mode': { + category: 'netflow', + name: 'netflow.sampler_mode', + type: 'short', + }, + 'netflow.sampler_random_interval': { + category: 'netflow', + name: 'netflow.sampler_random_interval', + type: 'long', + }, + 'netflow.class_id': { + category: 'netflow', + name: 'netflow.class_id', + type: 'long', + }, + 'netflow.minimum_ttl': { + category: 'netflow', + name: 'netflow.minimum_ttl', + type: 'short', + }, + 'netflow.maximum_ttl': { + category: 'netflow', + name: 'netflow.maximum_ttl', + type: 'short', + }, + 'netflow.fragment_identification': { + category: 'netflow', + name: 'netflow.fragment_identification', + type: 'long', + }, + 'netflow.post_ip_class_of_service': { + category: 'netflow', + name: 'netflow.post_ip_class_of_service', + type: 'short', + }, + 'netflow.source_mac_address': { + category: 'netflow', + name: 'netflow.source_mac_address', + type: 'keyword', + }, + 'netflow.post_destination_mac_address': { + category: 'netflow', + name: 'netflow.post_destination_mac_address', + type: 'keyword', + }, + 'netflow.vlan_id': { + category: 'netflow', + name: 'netflow.vlan_id', + type: 'integer', + }, + 'netflow.post_vlan_id': { + category: 'netflow', + name: 'netflow.post_vlan_id', + type: 'integer', + }, + 'netflow.ip_version': { + category: 'netflow', + name: 'netflow.ip_version', + type: 'short', + }, + 'netflow.flow_direction': { + category: 'netflow', + name: 'netflow.flow_direction', + type: 'short', + }, + 'netflow.ip_next_hop_ipv6_address': { + category: 'netflow', + name: 'netflow.ip_next_hop_ipv6_address', + type: 'ip', + }, + 'netflow.bgp_next_hop_ipv6_address': { + category: 'netflow', + name: 'netflow.bgp_next_hop_ipv6_address', + type: 'ip', + }, + 'netflow.ipv6_extension_headers': { + category: 'netflow', + name: 'netflow.ipv6_extension_headers', + type: 'long', + }, + 'netflow.mpls_top_label_stack_section': { + category: 'netflow', + name: 'netflow.mpls_top_label_stack_section', + type: 'short', + }, + 'netflow.mpls_label_stack_section2': { + category: 'netflow', + name: 'netflow.mpls_label_stack_section2', + type: 'short', + }, + 'netflow.mpls_label_stack_section3': { + category: 'netflow', + name: 'netflow.mpls_label_stack_section3', + type: 'short', + }, + 'netflow.mpls_label_stack_section4': { + category: 'netflow', + name: 'netflow.mpls_label_stack_section4', + type: 'short', + }, + 'netflow.mpls_label_stack_section5': { + category: 'netflow', + name: 'netflow.mpls_label_stack_section5', + type: 'short', + }, + 'netflow.mpls_label_stack_section6': { + category: 'netflow', + name: 'netflow.mpls_label_stack_section6', + type: 'short', + }, + 'netflow.mpls_label_stack_section7': { + category: 'netflow', + name: 'netflow.mpls_label_stack_section7', + type: 'short', + }, + 'netflow.mpls_label_stack_section8': { + category: 'netflow', + name: 'netflow.mpls_label_stack_section8', + type: 'short', + }, + 'netflow.mpls_label_stack_section9': { + category: 'netflow', + name: 'netflow.mpls_label_stack_section9', + type: 'short', + }, + 'netflow.mpls_label_stack_section10': { + category: 'netflow', + name: 'netflow.mpls_label_stack_section10', + type: 'short', + }, + 'netflow.destination_mac_address': { + category: 'netflow', + name: 'netflow.destination_mac_address', + type: 'keyword', + }, + 'netflow.post_source_mac_address': { + category: 'netflow', + name: 'netflow.post_source_mac_address', + type: 'keyword', + }, + 'netflow.interface_name': { + category: 'netflow', + name: 'netflow.interface_name', + type: 'keyword', + }, + 'netflow.interface_description': { + category: 'netflow', + name: 'netflow.interface_description', + type: 'keyword', + }, + 'netflow.sampler_name': { + category: 'netflow', + name: 'netflow.sampler_name', + type: 'keyword', + }, + 'netflow.octet_total_count': { + category: 'netflow', + name: 'netflow.octet_total_count', + type: 'long', + }, + 'netflow.packet_total_count': { + category: 'netflow', + name: 'netflow.packet_total_count', + type: 'long', + }, + 'netflow.flags_and_sampler_id': { + category: 'netflow', + name: 'netflow.flags_and_sampler_id', + type: 'long', + }, + 'netflow.fragment_offset': { + category: 'netflow', + name: 'netflow.fragment_offset', + type: 'integer', + }, + 'netflow.forwarding_status': { + category: 'netflow', + name: 'netflow.forwarding_status', + type: 'short', + }, + 'netflow.mpls_vpn_route_distinguisher': { + category: 'netflow', + name: 'netflow.mpls_vpn_route_distinguisher', + type: 'short', + }, + 'netflow.mpls_top_label_prefix_length': { + category: 'netflow', + name: 'netflow.mpls_top_label_prefix_length', + type: 'short', + }, + 'netflow.src_traffic_index': { + category: 'netflow', + name: 'netflow.src_traffic_index', + type: 'long', + }, + 'netflow.dst_traffic_index': { + category: 'netflow', + name: 'netflow.dst_traffic_index', + type: 'long', + }, + 'netflow.application_description': { + category: 'netflow', + name: 'netflow.application_description', + type: 'keyword', + }, + 'netflow.application_id': { + category: 'netflow', + name: 'netflow.application_id', + type: 'short', + }, + 'netflow.application_name': { + category: 'netflow', + name: 'netflow.application_name', + type: 'keyword', + }, + 'netflow.post_ip_diff_serv_code_point': { + category: 'netflow', + name: 'netflow.post_ip_diff_serv_code_point', + type: 'short', + }, + 'netflow.multicast_replication_factor': { + category: 'netflow', + name: 'netflow.multicast_replication_factor', + type: 'long', + }, + 'netflow.class_name': { + category: 'netflow', + name: 'netflow.class_name', + type: 'keyword', + }, + 'netflow.classification_engine_id': { + category: 'netflow', + name: 'netflow.classification_engine_id', + type: 'short', + }, + 'netflow.layer2packet_section_offset': { + category: 'netflow', + name: 'netflow.layer2packet_section_offset', + type: 'integer', + }, + 'netflow.layer2packet_section_size': { + category: 'netflow', + name: 'netflow.layer2packet_section_size', + type: 'integer', + }, + 'netflow.layer2packet_section_data': { + category: 'netflow', + name: 'netflow.layer2packet_section_data', + type: 'short', + }, + 'netflow.bgp_next_adjacent_as_number': { + category: 'netflow', + name: 'netflow.bgp_next_adjacent_as_number', + type: 'long', + }, + 'netflow.bgp_prev_adjacent_as_number': { + category: 'netflow', + name: 'netflow.bgp_prev_adjacent_as_number', + type: 'long', + }, + 'netflow.exporter_ipv4_address': { + category: 'netflow', + name: 'netflow.exporter_ipv4_address', + type: 'ip', + }, + 'netflow.exporter_ipv6_address': { + category: 'netflow', + name: 'netflow.exporter_ipv6_address', + type: 'ip', + }, + 'netflow.dropped_octet_delta_count': { + category: 'netflow', + name: 'netflow.dropped_octet_delta_count', + type: 'long', + }, + 'netflow.dropped_packet_delta_count': { + category: 'netflow', + name: 'netflow.dropped_packet_delta_count', + type: 'long', + }, + 'netflow.dropped_octet_total_count': { + category: 'netflow', + name: 'netflow.dropped_octet_total_count', + type: 'long', + }, + 'netflow.dropped_packet_total_count': { + category: 'netflow', + name: 'netflow.dropped_packet_total_count', + type: 'long', + }, + 'netflow.flow_end_reason': { + category: 'netflow', + name: 'netflow.flow_end_reason', + type: 'short', + }, + 'netflow.common_properties_id': { + category: 'netflow', + name: 'netflow.common_properties_id', + type: 'long', + }, + 'netflow.observation_point_id': { + category: 'netflow', + name: 'netflow.observation_point_id', + type: 'long', + }, + 'netflow.icmp_type_code_ipv6': { + category: 'netflow', + name: 'netflow.icmp_type_code_ipv6', + type: 'integer', + }, + 'netflow.mpls_top_label_ipv6_address': { + category: 'netflow', + name: 'netflow.mpls_top_label_ipv6_address', + type: 'ip', + }, + 'netflow.line_card_id': { + category: 'netflow', + name: 'netflow.line_card_id', + type: 'long', + }, + 'netflow.port_id': { + category: 'netflow', + name: 'netflow.port_id', + type: 'long', + }, + 'netflow.metering_process_id': { + category: 'netflow', + name: 'netflow.metering_process_id', + type: 'long', + }, + 'netflow.exporting_process_id': { + category: 'netflow', + name: 'netflow.exporting_process_id', + type: 'long', + }, + 'netflow.template_id': { + category: 'netflow', + name: 'netflow.template_id', + type: 'integer', + }, + 'netflow.wlan_channel_id': { + category: 'netflow', + name: 'netflow.wlan_channel_id', + type: 'short', + }, + 'netflow.wlan_ssid': { + category: 'netflow', + name: 'netflow.wlan_ssid', + type: 'keyword', + }, + 'netflow.flow_id': { + category: 'netflow', + name: 'netflow.flow_id', + type: 'long', + }, + 'netflow.observation_domain_id': { + category: 'netflow', + name: 'netflow.observation_domain_id', + type: 'long', + }, + 'netflow.flow_start_seconds': { + category: 'netflow', + name: 'netflow.flow_start_seconds', + type: 'date', + }, + 'netflow.flow_end_seconds': { + category: 'netflow', + name: 'netflow.flow_end_seconds', + type: 'date', + }, + 'netflow.flow_start_milliseconds': { + category: 'netflow', + name: 'netflow.flow_start_milliseconds', + type: 'date', + }, + 'netflow.flow_end_milliseconds': { + category: 'netflow', + name: 'netflow.flow_end_milliseconds', + type: 'date', + }, + 'netflow.flow_start_microseconds': { + category: 'netflow', + name: 'netflow.flow_start_microseconds', + type: 'date', + }, + 'netflow.flow_end_microseconds': { + category: 'netflow', + name: 'netflow.flow_end_microseconds', + type: 'date', + }, + 'netflow.flow_start_nanoseconds': { + category: 'netflow', + name: 'netflow.flow_start_nanoseconds', + type: 'date', + }, + 'netflow.flow_end_nanoseconds': { + category: 'netflow', + name: 'netflow.flow_end_nanoseconds', + type: 'date', + }, + 'netflow.flow_start_delta_microseconds': { + category: 'netflow', + name: 'netflow.flow_start_delta_microseconds', + type: 'long', + }, + 'netflow.flow_end_delta_microseconds': { + category: 'netflow', + name: 'netflow.flow_end_delta_microseconds', + type: 'long', + }, + 'netflow.system_init_time_milliseconds': { + category: 'netflow', + name: 'netflow.system_init_time_milliseconds', + type: 'date', + }, + 'netflow.flow_duration_milliseconds': { + category: 'netflow', + name: 'netflow.flow_duration_milliseconds', + type: 'long', + }, + 'netflow.flow_duration_microseconds': { + category: 'netflow', + name: 'netflow.flow_duration_microseconds', + type: 'long', + }, + 'netflow.observed_flow_total_count': { + category: 'netflow', + name: 'netflow.observed_flow_total_count', + type: 'long', + }, + 'netflow.ignored_packet_total_count': { + category: 'netflow', + name: 'netflow.ignored_packet_total_count', + type: 'long', + }, + 'netflow.ignored_octet_total_count': { + category: 'netflow', + name: 'netflow.ignored_octet_total_count', + type: 'long', + }, + 'netflow.not_sent_flow_total_count': { + category: 'netflow', + name: 'netflow.not_sent_flow_total_count', + type: 'long', + }, + 'netflow.not_sent_packet_total_count': { + category: 'netflow', + name: 'netflow.not_sent_packet_total_count', + type: 'long', + }, + 'netflow.not_sent_octet_total_count': { + category: 'netflow', + name: 'netflow.not_sent_octet_total_count', + type: 'long', + }, + 'netflow.destination_ipv6_prefix': { + category: 'netflow', + name: 'netflow.destination_ipv6_prefix', + type: 'ip', + }, + 'netflow.source_ipv6_prefix': { + category: 'netflow', + name: 'netflow.source_ipv6_prefix', + type: 'ip', + }, + 'netflow.post_octet_total_count': { + category: 'netflow', + name: 'netflow.post_octet_total_count', + type: 'long', + }, + 'netflow.post_packet_total_count': { + category: 'netflow', + name: 'netflow.post_packet_total_count', + type: 'long', + }, + 'netflow.flow_key_indicator': { + category: 'netflow', + name: 'netflow.flow_key_indicator', + type: 'long', + }, + 'netflow.post_mcast_packet_total_count': { + category: 'netflow', + name: 'netflow.post_mcast_packet_total_count', + type: 'long', + }, + 'netflow.post_mcast_octet_total_count': { + category: 'netflow', + name: 'netflow.post_mcast_octet_total_count', + type: 'long', + }, + 'netflow.icmp_type_ipv4': { + category: 'netflow', + name: 'netflow.icmp_type_ipv4', + type: 'short', + }, + 'netflow.icmp_code_ipv4': { + category: 'netflow', + name: 'netflow.icmp_code_ipv4', + type: 'short', + }, + 'netflow.icmp_type_ipv6': { + category: 'netflow', + name: 'netflow.icmp_type_ipv6', + type: 'short', + }, + 'netflow.icmp_code_ipv6': { + category: 'netflow', + name: 'netflow.icmp_code_ipv6', + type: 'short', + }, + 'netflow.udp_source_port': { + category: 'netflow', + name: 'netflow.udp_source_port', + type: 'integer', + }, + 'netflow.udp_destination_port': { + category: 'netflow', + name: 'netflow.udp_destination_port', + type: 'integer', + }, + 'netflow.tcp_source_port': { + category: 'netflow', + name: 'netflow.tcp_source_port', + type: 'integer', + }, + 'netflow.tcp_destination_port': { + category: 'netflow', + name: 'netflow.tcp_destination_port', + type: 'integer', + }, + 'netflow.tcp_sequence_number': { + category: 'netflow', + name: 'netflow.tcp_sequence_number', + type: 'long', + }, + 'netflow.tcp_acknowledgement_number': { + category: 'netflow', + name: 'netflow.tcp_acknowledgement_number', + type: 'long', + }, + 'netflow.tcp_window_size': { + category: 'netflow', + name: 'netflow.tcp_window_size', + type: 'integer', + }, + 'netflow.tcp_urgent_pointer': { + category: 'netflow', + name: 'netflow.tcp_urgent_pointer', + type: 'integer', + }, + 'netflow.tcp_header_length': { + category: 'netflow', + name: 'netflow.tcp_header_length', + type: 'short', + }, + 'netflow.ip_header_length': { + category: 'netflow', + name: 'netflow.ip_header_length', + type: 'short', + }, + 'netflow.total_length_ipv4': { + category: 'netflow', + name: 'netflow.total_length_ipv4', + type: 'integer', + }, + 'netflow.payload_length_ipv6': { + category: 'netflow', + name: 'netflow.payload_length_ipv6', + type: 'integer', + }, + 'netflow.ip_ttl': { + category: 'netflow', + name: 'netflow.ip_ttl', + type: 'short', + }, + 'netflow.next_header_ipv6': { + category: 'netflow', + name: 'netflow.next_header_ipv6', + type: 'short', + }, + 'netflow.mpls_payload_length': { + category: 'netflow', + name: 'netflow.mpls_payload_length', + type: 'long', + }, + 'netflow.ip_diff_serv_code_point': { + category: 'netflow', + name: 'netflow.ip_diff_serv_code_point', + type: 'short', + }, + 'netflow.ip_precedence': { + category: 'netflow', + name: 'netflow.ip_precedence', + type: 'short', + }, + 'netflow.fragment_flags': { + category: 'netflow', + name: 'netflow.fragment_flags', + type: 'short', + }, + 'netflow.octet_delta_sum_of_squares': { + category: 'netflow', + name: 'netflow.octet_delta_sum_of_squares', + type: 'long', + }, + 'netflow.octet_total_sum_of_squares': { + category: 'netflow', + name: 'netflow.octet_total_sum_of_squares', + type: 'long', + }, + 'netflow.mpls_top_label_ttl': { + category: 'netflow', + name: 'netflow.mpls_top_label_ttl', + type: 'short', + }, + 'netflow.mpls_label_stack_length': { + category: 'netflow', + name: 'netflow.mpls_label_stack_length', + type: 'long', + }, + 'netflow.mpls_label_stack_depth': { + category: 'netflow', + name: 'netflow.mpls_label_stack_depth', + type: 'long', + }, + 'netflow.mpls_top_label_exp': { + category: 'netflow', + name: 'netflow.mpls_top_label_exp', + type: 'short', + }, + 'netflow.ip_payload_length': { + category: 'netflow', + name: 'netflow.ip_payload_length', + type: 'long', + }, + 'netflow.udp_message_length': { + category: 'netflow', + name: 'netflow.udp_message_length', + type: 'integer', + }, + 'netflow.is_multicast': { + category: 'netflow', + name: 'netflow.is_multicast', + type: 'short', + }, + 'netflow.ipv4_ihl': { + category: 'netflow', + name: 'netflow.ipv4_ihl', + type: 'short', + }, + 'netflow.ipv4_options': { + category: 'netflow', + name: 'netflow.ipv4_options', + type: 'long', + }, + 'netflow.tcp_options': { + category: 'netflow', + name: 'netflow.tcp_options', + type: 'long', + }, + 'netflow.padding_octets': { + category: 'netflow', + name: 'netflow.padding_octets', + type: 'short', + }, + 'netflow.collector_ipv4_address': { + category: 'netflow', + name: 'netflow.collector_ipv4_address', + type: 'ip', + }, + 'netflow.collector_ipv6_address': { + category: 'netflow', + name: 'netflow.collector_ipv6_address', + type: 'ip', + }, + 'netflow.export_interface': { + category: 'netflow', + name: 'netflow.export_interface', + type: 'long', + }, + 'netflow.export_protocol_version': { + category: 'netflow', + name: 'netflow.export_protocol_version', + type: 'short', + }, + 'netflow.export_transport_protocol': { + category: 'netflow', + name: 'netflow.export_transport_protocol', + type: 'short', + }, + 'netflow.collector_transport_port': { + category: 'netflow', + name: 'netflow.collector_transport_port', + type: 'integer', + }, + 'netflow.exporter_transport_port': { + category: 'netflow', + name: 'netflow.exporter_transport_port', + type: 'integer', + }, + 'netflow.tcp_syn_total_count': { + category: 'netflow', + name: 'netflow.tcp_syn_total_count', + type: 'long', + }, + 'netflow.tcp_fin_total_count': { + category: 'netflow', + name: 'netflow.tcp_fin_total_count', + type: 'long', + }, + 'netflow.tcp_rst_total_count': { + category: 'netflow', + name: 'netflow.tcp_rst_total_count', + type: 'long', + }, + 'netflow.tcp_psh_total_count': { + category: 'netflow', + name: 'netflow.tcp_psh_total_count', + type: 'long', + }, + 'netflow.tcp_ack_total_count': { + category: 'netflow', + name: 'netflow.tcp_ack_total_count', + type: 'long', + }, + 'netflow.tcp_urg_total_count': { + category: 'netflow', + name: 'netflow.tcp_urg_total_count', + type: 'long', + }, + 'netflow.ip_total_length': { + category: 'netflow', + name: 'netflow.ip_total_length', + type: 'long', + }, + 'netflow.post_nat_source_ipv4_address': { + category: 'netflow', + name: 'netflow.post_nat_source_ipv4_address', + type: 'ip', + }, + 'netflow.post_nat_destination_ipv4_address': { + category: 'netflow', + name: 'netflow.post_nat_destination_ipv4_address', + type: 'ip', + }, + 'netflow.post_napt_source_transport_port': { + category: 'netflow', + name: 'netflow.post_napt_source_transport_port', + type: 'integer', + }, + 'netflow.post_napt_destination_transport_port': { + category: 'netflow', + name: 'netflow.post_napt_destination_transport_port', + type: 'integer', + }, + 'netflow.nat_originating_address_realm': { + category: 'netflow', + name: 'netflow.nat_originating_address_realm', + type: 'short', + }, + 'netflow.nat_event': { + category: 'netflow', + name: 'netflow.nat_event', + type: 'short', + }, + 'netflow.initiator_octets': { + category: 'netflow', + name: 'netflow.initiator_octets', + type: 'long', + }, + 'netflow.responder_octets': { + category: 'netflow', + name: 'netflow.responder_octets', + type: 'long', + }, + 'netflow.firewall_event': { + category: 'netflow', + name: 'netflow.firewall_event', + type: 'short', + }, + 'netflow.ingress_vrfid': { + category: 'netflow', + name: 'netflow.ingress_vrfid', + type: 'long', + }, + 'netflow.egress_vrfid': { + category: 'netflow', + name: 'netflow.egress_vrfid', + type: 'long', + }, + 'netflow.vr_fname': { + category: 'netflow', + name: 'netflow.vr_fname', + type: 'keyword', + }, + 'netflow.post_mpls_top_label_exp': { + category: 'netflow', + name: 'netflow.post_mpls_top_label_exp', + type: 'short', + }, + 'netflow.tcp_window_scale': { + category: 'netflow', + name: 'netflow.tcp_window_scale', + type: 'integer', + }, + 'netflow.biflow_direction': { + category: 'netflow', + name: 'netflow.biflow_direction', + type: 'short', + }, + 'netflow.ethernet_header_length': { + category: 'netflow', + name: 'netflow.ethernet_header_length', + type: 'short', + }, + 'netflow.ethernet_payload_length': { + category: 'netflow', + name: 'netflow.ethernet_payload_length', + type: 'integer', + }, + 'netflow.ethernet_total_length': { + category: 'netflow', + name: 'netflow.ethernet_total_length', + type: 'integer', + }, + 'netflow.dot1q_vlan_id': { + category: 'netflow', + name: 'netflow.dot1q_vlan_id', + type: 'integer', + }, + 'netflow.dot1q_priority': { + category: 'netflow', + name: 'netflow.dot1q_priority', + type: 'short', + }, + 'netflow.dot1q_customer_vlan_id': { + category: 'netflow', + name: 'netflow.dot1q_customer_vlan_id', + type: 'integer', + }, + 'netflow.dot1q_customer_priority': { + category: 'netflow', + name: 'netflow.dot1q_customer_priority', + type: 'short', + }, + 'netflow.metro_evc_id': { + category: 'netflow', + name: 'netflow.metro_evc_id', + type: 'keyword', + }, + 'netflow.metro_evc_type': { + category: 'netflow', + name: 'netflow.metro_evc_type', + type: 'short', + }, + 'netflow.pseudo_wire_id': { + category: 'netflow', + name: 'netflow.pseudo_wire_id', + type: 'long', + }, + 'netflow.pseudo_wire_type': { + category: 'netflow', + name: 'netflow.pseudo_wire_type', + type: 'integer', + }, + 'netflow.pseudo_wire_control_word': { + category: 'netflow', + name: 'netflow.pseudo_wire_control_word', + type: 'long', + }, + 'netflow.ingress_physical_interface': { + category: 'netflow', + name: 'netflow.ingress_physical_interface', + type: 'long', + }, + 'netflow.egress_physical_interface': { + category: 'netflow', + name: 'netflow.egress_physical_interface', + type: 'long', + }, + 'netflow.post_dot1q_vlan_id': { + category: 'netflow', + name: 'netflow.post_dot1q_vlan_id', + type: 'integer', + }, + 'netflow.post_dot1q_customer_vlan_id': { + category: 'netflow', + name: 'netflow.post_dot1q_customer_vlan_id', + type: 'integer', + }, + 'netflow.ethernet_type': { + category: 'netflow', + name: 'netflow.ethernet_type', + type: 'integer', + }, + 'netflow.post_ip_precedence': { + category: 'netflow', + name: 'netflow.post_ip_precedence', + type: 'short', + }, + 'netflow.collection_time_milliseconds': { + category: 'netflow', + name: 'netflow.collection_time_milliseconds', + type: 'date', + }, + 'netflow.export_sctp_stream_id': { + category: 'netflow', + name: 'netflow.export_sctp_stream_id', + type: 'integer', + }, + 'netflow.max_export_seconds': { + category: 'netflow', + name: 'netflow.max_export_seconds', + type: 'date', + }, + 'netflow.max_flow_end_seconds': { + category: 'netflow', + name: 'netflow.max_flow_end_seconds', + type: 'date', + }, + 'netflow.message_md5_checksum': { + category: 'netflow', + name: 'netflow.message_md5_checksum', + type: 'short', + }, + 'netflow.message_scope': { + category: 'netflow', + name: 'netflow.message_scope', + type: 'short', + }, + 'netflow.min_export_seconds': { + category: 'netflow', + name: 'netflow.min_export_seconds', + type: 'date', + }, + 'netflow.min_flow_start_seconds': { + category: 'netflow', + name: 'netflow.min_flow_start_seconds', + type: 'date', + }, + 'netflow.opaque_octets': { + category: 'netflow', + name: 'netflow.opaque_octets', + type: 'short', + }, + 'netflow.session_scope': { + category: 'netflow', + name: 'netflow.session_scope', + type: 'short', + }, + 'netflow.max_flow_end_microseconds': { + category: 'netflow', + name: 'netflow.max_flow_end_microseconds', + type: 'date', + }, + 'netflow.max_flow_end_milliseconds': { + category: 'netflow', + name: 'netflow.max_flow_end_milliseconds', + type: 'date', + }, + 'netflow.max_flow_end_nanoseconds': { + category: 'netflow', + name: 'netflow.max_flow_end_nanoseconds', + type: 'date', + }, + 'netflow.min_flow_start_microseconds': { + category: 'netflow', + name: 'netflow.min_flow_start_microseconds', + type: 'date', + }, + 'netflow.min_flow_start_milliseconds': { + category: 'netflow', + name: 'netflow.min_flow_start_milliseconds', + type: 'date', + }, + 'netflow.min_flow_start_nanoseconds': { + category: 'netflow', + name: 'netflow.min_flow_start_nanoseconds', + type: 'date', + }, + 'netflow.collector_certificate': { + category: 'netflow', + name: 'netflow.collector_certificate', + type: 'short', + }, + 'netflow.exporter_certificate': { + category: 'netflow', + name: 'netflow.exporter_certificate', + type: 'short', + }, + 'netflow.data_records_reliability': { + category: 'netflow', + name: 'netflow.data_records_reliability', + type: 'boolean', + }, + 'netflow.observation_point_type': { + category: 'netflow', + name: 'netflow.observation_point_type', + type: 'short', + }, + 'netflow.new_connection_delta_count': { + category: 'netflow', + name: 'netflow.new_connection_delta_count', + type: 'long', + }, + 'netflow.connection_sum_duration_seconds': { + category: 'netflow', + name: 'netflow.connection_sum_duration_seconds', + type: 'long', + }, + 'netflow.connection_transaction_id': { + category: 'netflow', + name: 'netflow.connection_transaction_id', + type: 'long', + }, + 'netflow.post_nat_source_ipv6_address': { + category: 'netflow', + name: 'netflow.post_nat_source_ipv6_address', + type: 'ip', + }, + 'netflow.post_nat_destination_ipv6_address': { + category: 'netflow', + name: 'netflow.post_nat_destination_ipv6_address', + type: 'ip', + }, + 'netflow.nat_pool_id': { + category: 'netflow', + name: 'netflow.nat_pool_id', + type: 'long', + }, + 'netflow.nat_pool_name': { + category: 'netflow', + name: 'netflow.nat_pool_name', + type: 'keyword', + }, + 'netflow.anonymization_flags': { + category: 'netflow', + name: 'netflow.anonymization_flags', + type: 'integer', + }, + 'netflow.anonymization_technique': { + category: 'netflow', + name: 'netflow.anonymization_technique', + type: 'integer', + }, + 'netflow.information_element_index': { + category: 'netflow', + name: 'netflow.information_element_index', + type: 'integer', + }, + 'netflow.p2p_technology': { + category: 'netflow', + name: 'netflow.p2p_technology', + type: 'keyword', + }, + 'netflow.tunnel_technology': { + category: 'netflow', + name: 'netflow.tunnel_technology', + type: 'keyword', + }, + 'netflow.encrypted_technology': { + category: 'netflow', + name: 'netflow.encrypted_technology', + type: 'keyword', + }, + 'netflow.bgp_validity_state': { + category: 'netflow', + name: 'netflow.bgp_validity_state', + type: 'short', + }, + 'netflow.ip_sec_spi': { + category: 'netflow', + name: 'netflow.ip_sec_spi', + type: 'long', + }, + 'netflow.gre_key': { + category: 'netflow', + name: 'netflow.gre_key', + type: 'long', + }, + 'netflow.nat_type': { + category: 'netflow', + name: 'netflow.nat_type', + type: 'short', + }, + 'netflow.initiator_packets': { + category: 'netflow', + name: 'netflow.initiator_packets', + type: 'long', + }, + 'netflow.responder_packets': { + category: 'netflow', + name: 'netflow.responder_packets', + type: 'long', + }, + 'netflow.observation_domain_name': { + category: 'netflow', + name: 'netflow.observation_domain_name', + type: 'keyword', + }, + 'netflow.selection_sequence_id': { + category: 'netflow', + name: 'netflow.selection_sequence_id', + type: 'long', + }, + 'netflow.selector_id': { + category: 'netflow', + name: 'netflow.selector_id', + type: 'long', + }, + 'netflow.information_element_id': { + category: 'netflow', + name: 'netflow.information_element_id', + type: 'integer', + }, + 'netflow.selector_algorithm': { + category: 'netflow', + name: 'netflow.selector_algorithm', + type: 'integer', + }, + 'netflow.sampling_packet_interval': { + category: 'netflow', + name: 'netflow.sampling_packet_interval', + type: 'long', + }, + 'netflow.sampling_packet_space': { + category: 'netflow', + name: 'netflow.sampling_packet_space', + type: 'long', + }, + 'netflow.sampling_time_interval': { + category: 'netflow', + name: 'netflow.sampling_time_interval', + type: 'long', + }, + 'netflow.sampling_time_space': { + category: 'netflow', + name: 'netflow.sampling_time_space', + type: 'long', + }, + 'netflow.sampling_size': { + category: 'netflow', + name: 'netflow.sampling_size', + type: 'long', + }, + 'netflow.sampling_population': { + category: 'netflow', + name: 'netflow.sampling_population', + type: 'long', + }, + 'netflow.sampling_probability': { + category: 'netflow', + name: 'netflow.sampling_probability', + type: 'double', + }, + 'netflow.data_link_frame_size': { + category: 'netflow', + name: 'netflow.data_link_frame_size', + type: 'integer', + }, + 'netflow.ip_header_packet_section': { + category: 'netflow', + name: 'netflow.ip_header_packet_section', + type: 'short', + }, + 'netflow.ip_payload_packet_section': { + category: 'netflow', + name: 'netflow.ip_payload_packet_section', + type: 'short', + }, + 'netflow.data_link_frame_section': { + category: 'netflow', + name: 'netflow.data_link_frame_section', + type: 'short', + }, + 'netflow.mpls_label_stack_section': { + category: 'netflow', + name: 'netflow.mpls_label_stack_section', + type: 'short', + }, + 'netflow.mpls_payload_packet_section': { + category: 'netflow', + name: 'netflow.mpls_payload_packet_section', + type: 'short', + }, + 'netflow.selector_id_total_pkts_observed': { + category: 'netflow', + name: 'netflow.selector_id_total_pkts_observed', + type: 'long', + }, + 'netflow.selector_id_total_pkts_selected': { + category: 'netflow', + name: 'netflow.selector_id_total_pkts_selected', + type: 'long', + }, + 'netflow.absolute_error': { + category: 'netflow', + name: 'netflow.absolute_error', + type: 'double', + }, + 'netflow.relative_error': { + category: 'netflow', + name: 'netflow.relative_error', + type: 'double', + }, + 'netflow.observation_time_seconds': { + category: 'netflow', + name: 'netflow.observation_time_seconds', + type: 'date', + }, + 'netflow.observation_time_milliseconds': { + category: 'netflow', + name: 'netflow.observation_time_milliseconds', + type: 'date', + }, + 'netflow.observation_time_microseconds': { + category: 'netflow', + name: 'netflow.observation_time_microseconds', + type: 'date', + }, + 'netflow.observation_time_nanoseconds': { + category: 'netflow', + name: 'netflow.observation_time_nanoseconds', + type: 'date', + }, + 'netflow.digest_hash_value': { + category: 'netflow', + name: 'netflow.digest_hash_value', + type: 'long', + }, + 'netflow.hash_ip_payload_offset': { + category: 'netflow', + name: 'netflow.hash_ip_payload_offset', + type: 'long', + }, + 'netflow.hash_ip_payload_size': { + category: 'netflow', + name: 'netflow.hash_ip_payload_size', + type: 'long', + }, + 'netflow.hash_output_range_min': { + category: 'netflow', + name: 'netflow.hash_output_range_min', + type: 'long', + }, + 'netflow.hash_output_range_max': { + category: 'netflow', + name: 'netflow.hash_output_range_max', + type: 'long', + }, + 'netflow.hash_selected_range_min': { + category: 'netflow', + name: 'netflow.hash_selected_range_min', + type: 'long', + }, + 'netflow.hash_selected_range_max': { + category: 'netflow', + name: 'netflow.hash_selected_range_max', + type: 'long', + }, + 'netflow.hash_digest_output': { + category: 'netflow', + name: 'netflow.hash_digest_output', + type: 'boolean', + }, + 'netflow.hash_initialiser_value': { + category: 'netflow', + name: 'netflow.hash_initialiser_value', + type: 'long', + }, + 'netflow.selector_name': { + category: 'netflow', + name: 'netflow.selector_name', + type: 'keyword', + }, + 'netflow.upper_ci_limit': { + category: 'netflow', + name: 'netflow.upper_ci_limit', + type: 'double', + }, + 'netflow.lower_ci_limit': { + category: 'netflow', + name: 'netflow.lower_ci_limit', + type: 'double', + }, + 'netflow.confidence_level': { + category: 'netflow', + name: 'netflow.confidence_level', + type: 'double', + }, + 'netflow.information_element_data_type': { + category: 'netflow', + name: 'netflow.information_element_data_type', + type: 'short', + }, + 'netflow.information_element_description': { + category: 'netflow', + name: 'netflow.information_element_description', + type: 'keyword', + }, + 'netflow.information_element_name': { + category: 'netflow', + name: 'netflow.information_element_name', + type: 'keyword', + }, + 'netflow.information_element_range_begin': { + category: 'netflow', + name: 'netflow.information_element_range_begin', + type: 'long', + }, + 'netflow.information_element_range_end': { + category: 'netflow', + name: 'netflow.information_element_range_end', + type: 'long', + }, + 'netflow.information_element_semantics': { + category: 'netflow', + name: 'netflow.information_element_semantics', + type: 'short', + }, + 'netflow.information_element_units': { + category: 'netflow', + name: 'netflow.information_element_units', + type: 'integer', + }, + 'netflow.private_enterprise_number': { + category: 'netflow', + name: 'netflow.private_enterprise_number', + type: 'long', + }, + 'netflow.virtual_station_interface_id': { + category: 'netflow', + name: 'netflow.virtual_station_interface_id', + type: 'short', + }, + 'netflow.virtual_station_interface_name': { + category: 'netflow', + name: 'netflow.virtual_station_interface_name', + type: 'keyword', + }, + 'netflow.virtual_station_uuid': { + category: 'netflow', + name: 'netflow.virtual_station_uuid', + type: 'short', + }, + 'netflow.virtual_station_name': { + category: 'netflow', + name: 'netflow.virtual_station_name', + type: 'keyword', + }, + 'netflow.layer2_segment_id': { + category: 'netflow', + name: 'netflow.layer2_segment_id', + type: 'long', + }, + 'netflow.layer2_octet_delta_count': { + category: 'netflow', + name: 'netflow.layer2_octet_delta_count', + type: 'long', + }, + 'netflow.layer2_octet_total_count': { + category: 'netflow', + name: 'netflow.layer2_octet_total_count', + type: 'long', + }, + 'netflow.ingress_unicast_packet_total_count': { + category: 'netflow', + name: 'netflow.ingress_unicast_packet_total_count', + type: 'long', + }, + 'netflow.ingress_multicast_packet_total_count': { + category: 'netflow', + name: 'netflow.ingress_multicast_packet_total_count', + type: 'long', + }, + 'netflow.ingress_broadcast_packet_total_count': { + category: 'netflow', + name: 'netflow.ingress_broadcast_packet_total_count', + type: 'long', + }, + 'netflow.egress_unicast_packet_total_count': { + category: 'netflow', + name: 'netflow.egress_unicast_packet_total_count', + type: 'long', + }, + 'netflow.egress_broadcast_packet_total_count': { + category: 'netflow', + name: 'netflow.egress_broadcast_packet_total_count', + type: 'long', + }, + 'netflow.monitoring_interval_start_milli_seconds': { + category: 'netflow', + name: 'netflow.monitoring_interval_start_milli_seconds', + type: 'date', + }, + 'netflow.monitoring_interval_end_milli_seconds': { + category: 'netflow', + name: 'netflow.monitoring_interval_end_milli_seconds', + type: 'date', + }, + 'netflow.port_range_start': { + category: 'netflow', + name: 'netflow.port_range_start', + type: 'integer', + }, + 'netflow.port_range_end': { + category: 'netflow', + name: 'netflow.port_range_end', + type: 'integer', + }, + 'netflow.port_range_step_size': { + category: 'netflow', + name: 'netflow.port_range_step_size', + type: 'integer', + }, + 'netflow.port_range_num_ports': { + category: 'netflow', + name: 'netflow.port_range_num_ports', + type: 'integer', + }, + 'netflow.sta_mac_address': { + category: 'netflow', + name: 'netflow.sta_mac_address', + type: 'keyword', + }, + 'netflow.sta_ipv4_address': { + category: 'netflow', + name: 'netflow.sta_ipv4_address', + type: 'ip', + }, + 'netflow.wtp_mac_address': { + category: 'netflow', + name: 'netflow.wtp_mac_address', + type: 'keyword', + }, + 'netflow.ingress_interface_type': { + category: 'netflow', + name: 'netflow.ingress_interface_type', + type: 'long', + }, + 'netflow.egress_interface_type': { + category: 'netflow', + name: 'netflow.egress_interface_type', + type: 'long', + }, + 'netflow.rtp_sequence_number': { + category: 'netflow', + name: 'netflow.rtp_sequence_number', + type: 'integer', + }, + 'netflow.user_name': { + category: 'netflow', + name: 'netflow.user_name', + type: 'keyword', + }, + 'netflow.application_category_name': { + category: 'netflow', + name: 'netflow.application_category_name', + type: 'keyword', + }, + 'netflow.application_sub_category_name': { + category: 'netflow', + name: 'netflow.application_sub_category_name', + type: 'keyword', + }, + 'netflow.application_group_name': { + category: 'netflow', + name: 'netflow.application_group_name', + type: 'keyword', + }, + 'netflow.original_flows_present': { + category: 'netflow', + name: 'netflow.original_flows_present', + type: 'long', + }, + 'netflow.original_flows_initiated': { + category: 'netflow', + name: 'netflow.original_flows_initiated', + type: 'long', + }, + 'netflow.original_flows_completed': { + category: 'netflow', + name: 'netflow.original_flows_completed', + type: 'long', + }, + 'netflow.distinct_count_of_source_ip_address': { + category: 'netflow', + name: 'netflow.distinct_count_of_source_ip_address', + type: 'long', + }, + 'netflow.distinct_count_of_destination_ip_address': { + category: 'netflow', + name: 'netflow.distinct_count_of_destination_ip_address', + type: 'long', + }, + 'netflow.distinct_count_of_source_ipv4_address': { + category: 'netflow', + name: 'netflow.distinct_count_of_source_ipv4_address', + type: 'long', + }, + 'netflow.distinct_count_of_destination_ipv4_address': { + category: 'netflow', + name: 'netflow.distinct_count_of_destination_ipv4_address', + type: 'long', + }, + 'netflow.distinct_count_of_source_ipv6_address': { + category: 'netflow', + name: 'netflow.distinct_count_of_source_ipv6_address', + type: 'long', + }, + 'netflow.distinct_count_of_destination_ipv6_address': { + category: 'netflow', + name: 'netflow.distinct_count_of_destination_ipv6_address', + type: 'long', + }, + 'netflow.value_distribution_method': { + category: 'netflow', + name: 'netflow.value_distribution_method', + type: 'short', + }, + 'netflow.rfc3550_jitter_milliseconds': { + category: 'netflow', + name: 'netflow.rfc3550_jitter_milliseconds', + type: 'long', + }, + 'netflow.rfc3550_jitter_microseconds': { + category: 'netflow', + name: 'netflow.rfc3550_jitter_microseconds', + type: 'long', + }, + 'netflow.rfc3550_jitter_nanoseconds': { + category: 'netflow', + name: 'netflow.rfc3550_jitter_nanoseconds', + type: 'long', + }, + 'netflow.dot1q_dei': { + category: 'netflow', + name: 'netflow.dot1q_dei', + type: 'boolean', + }, + 'netflow.dot1q_customer_dei': { + category: 'netflow', + name: 'netflow.dot1q_customer_dei', + type: 'boolean', + }, + 'netflow.flow_selector_algorithm': { + category: 'netflow', + name: 'netflow.flow_selector_algorithm', + type: 'integer', + }, + 'netflow.flow_selected_octet_delta_count': { + category: 'netflow', + name: 'netflow.flow_selected_octet_delta_count', + type: 'long', + }, + 'netflow.flow_selected_packet_delta_count': { + category: 'netflow', + name: 'netflow.flow_selected_packet_delta_count', + type: 'long', + }, + 'netflow.flow_selected_flow_delta_count': { + category: 'netflow', + name: 'netflow.flow_selected_flow_delta_count', + type: 'long', + }, + 'netflow.selector_id_total_flows_observed': { + category: 'netflow', + name: 'netflow.selector_id_total_flows_observed', + type: 'long', + }, + 'netflow.selector_id_total_flows_selected': { + category: 'netflow', + name: 'netflow.selector_id_total_flows_selected', + type: 'long', + }, + 'netflow.sampling_flow_interval': { + category: 'netflow', + name: 'netflow.sampling_flow_interval', + type: 'long', + }, + 'netflow.sampling_flow_spacing': { + category: 'netflow', + name: 'netflow.sampling_flow_spacing', + type: 'long', + }, + 'netflow.flow_sampling_time_interval': { + category: 'netflow', + name: 'netflow.flow_sampling_time_interval', + type: 'long', + }, + 'netflow.flow_sampling_time_spacing': { + category: 'netflow', + name: 'netflow.flow_sampling_time_spacing', + type: 'long', + }, + 'netflow.hash_flow_domain': { + category: 'netflow', + name: 'netflow.hash_flow_domain', + type: 'integer', + }, + 'netflow.transport_octet_delta_count': { + category: 'netflow', + name: 'netflow.transport_octet_delta_count', + type: 'long', + }, + 'netflow.transport_packet_delta_count': { + category: 'netflow', + name: 'netflow.transport_packet_delta_count', + type: 'long', + }, + 'netflow.original_exporter_ipv4_address': { + category: 'netflow', + name: 'netflow.original_exporter_ipv4_address', + type: 'ip', + }, + 'netflow.original_exporter_ipv6_address': { + category: 'netflow', + name: 'netflow.original_exporter_ipv6_address', + type: 'ip', + }, + 'netflow.original_observation_domain_id': { + category: 'netflow', + name: 'netflow.original_observation_domain_id', + type: 'long', + }, + 'netflow.intermediate_process_id': { + category: 'netflow', + name: 'netflow.intermediate_process_id', + type: 'long', + }, + 'netflow.ignored_data_record_total_count': { + category: 'netflow', + name: 'netflow.ignored_data_record_total_count', + type: 'long', + }, + 'netflow.data_link_frame_type': { + category: 'netflow', + name: 'netflow.data_link_frame_type', + type: 'integer', + }, + 'netflow.section_offset': { + category: 'netflow', + name: 'netflow.section_offset', + type: 'integer', + }, + 'netflow.section_exported_octets': { + category: 'netflow', + name: 'netflow.section_exported_octets', + type: 'integer', + }, + 'netflow.dot1q_service_instance_tag': { + category: 'netflow', + name: 'netflow.dot1q_service_instance_tag', + type: 'short', + }, + 'netflow.dot1q_service_instance_id': { + category: 'netflow', + name: 'netflow.dot1q_service_instance_id', + type: 'long', + }, + 'netflow.dot1q_service_instance_priority': { + category: 'netflow', + name: 'netflow.dot1q_service_instance_priority', + type: 'short', + }, + 'netflow.dot1q_customer_source_mac_address': { + category: 'netflow', + name: 'netflow.dot1q_customer_source_mac_address', + type: 'keyword', + }, + 'netflow.dot1q_customer_destination_mac_address': { + category: 'netflow', + name: 'netflow.dot1q_customer_destination_mac_address', + type: 'keyword', + }, + 'netflow.post_layer2_octet_delta_count': { + category: 'netflow', + name: 'netflow.post_layer2_octet_delta_count', + type: 'long', + }, + 'netflow.post_mcast_layer2_octet_delta_count': { + category: 'netflow', + name: 'netflow.post_mcast_layer2_octet_delta_count', + type: 'long', + }, + 'netflow.post_layer2_octet_total_count': { + category: 'netflow', + name: 'netflow.post_layer2_octet_total_count', + type: 'long', + }, + 'netflow.post_mcast_layer2_octet_total_count': { + category: 'netflow', + name: 'netflow.post_mcast_layer2_octet_total_count', + type: 'long', + }, + 'netflow.minimum_layer2_total_length': { + category: 'netflow', + name: 'netflow.minimum_layer2_total_length', + type: 'long', + }, + 'netflow.maximum_layer2_total_length': { + category: 'netflow', + name: 'netflow.maximum_layer2_total_length', + type: 'long', + }, + 'netflow.dropped_layer2_octet_delta_count': { + category: 'netflow', + name: 'netflow.dropped_layer2_octet_delta_count', + type: 'long', + }, + 'netflow.dropped_layer2_octet_total_count': { + category: 'netflow', + name: 'netflow.dropped_layer2_octet_total_count', + type: 'long', + }, + 'netflow.ignored_layer2_octet_total_count': { + category: 'netflow', + name: 'netflow.ignored_layer2_octet_total_count', + type: 'long', + }, + 'netflow.not_sent_layer2_octet_total_count': { + category: 'netflow', + name: 'netflow.not_sent_layer2_octet_total_count', + type: 'long', + }, + 'netflow.layer2_octet_delta_sum_of_squares': { + category: 'netflow', + name: 'netflow.layer2_octet_delta_sum_of_squares', + type: 'long', + }, + 'netflow.layer2_octet_total_sum_of_squares': { + category: 'netflow', + name: 'netflow.layer2_octet_total_sum_of_squares', + type: 'long', + }, + 'netflow.layer2_frame_delta_count': { + category: 'netflow', + name: 'netflow.layer2_frame_delta_count', + type: 'long', + }, + 'netflow.layer2_frame_total_count': { + category: 'netflow', + name: 'netflow.layer2_frame_total_count', + type: 'long', + }, + 'netflow.pseudo_wire_destination_ipv4_address': { + category: 'netflow', + name: 'netflow.pseudo_wire_destination_ipv4_address', + type: 'ip', + }, + 'netflow.ignored_layer2_frame_total_count': { + category: 'netflow', + name: 'netflow.ignored_layer2_frame_total_count', + type: 'long', + }, + 'netflow.mib_object_value_integer': { + category: 'netflow', + name: 'netflow.mib_object_value_integer', + type: 'integer', + }, + 'netflow.mib_object_value_octet_string': { + category: 'netflow', + name: 'netflow.mib_object_value_octet_string', + type: 'short', + }, + 'netflow.mib_object_value_oid': { + category: 'netflow', + name: 'netflow.mib_object_value_oid', + type: 'short', + }, + 'netflow.mib_object_value_bits': { + category: 'netflow', + name: 'netflow.mib_object_value_bits', + type: 'short', + }, + 'netflow.mib_object_value_ip_address': { + category: 'netflow', + name: 'netflow.mib_object_value_ip_address', + type: 'ip', + }, + 'netflow.mib_object_value_counter': { + category: 'netflow', + name: 'netflow.mib_object_value_counter', + type: 'long', + }, + 'netflow.mib_object_value_gauge': { + category: 'netflow', + name: 'netflow.mib_object_value_gauge', + type: 'long', + }, + 'netflow.mib_object_value_time_ticks': { + category: 'netflow', + name: 'netflow.mib_object_value_time_ticks', + type: 'long', + }, + 'netflow.mib_object_value_unsigned': { + category: 'netflow', + name: 'netflow.mib_object_value_unsigned', + type: 'long', + }, + 'netflow.mib_object_identifier': { + category: 'netflow', + name: 'netflow.mib_object_identifier', + type: 'short', + }, + 'netflow.mib_sub_identifier': { + category: 'netflow', + name: 'netflow.mib_sub_identifier', + type: 'long', + }, + 'netflow.mib_index_indicator': { + category: 'netflow', + name: 'netflow.mib_index_indicator', + type: 'long', + }, + 'netflow.mib_capture_time_semantics': { + category: 'netflow', + name: 'netflow.mib_capture_time_semantics', + type: 'short', + }, + 'netflow.mib_context_engine_id': { + category: 'netflow', + name: 'netflow.mib_context_engine_id', + type: 'short', + }, + 'netflow.mib_context_name': { + category: 'netflow', + name: 'netflow.mib_context_name', + type: 'keyword', + }, + 'netflow.mib_object_name': { + category: 'netflow', + name: 'netflow.mib_object_name', + type: 'keyword', + }, + 'netflow.mib_object_description': { + category: 'netflow', + name: 'netflow.mib_object_description', + type: 'keyword', + }, + 'netflow.mib_object_syntax': { + category: 'netflow', + name: 'netflow.mib_object_syntax', + type: 'keyword', + }, + 'netflow.mib_module_name': { + category: 'netflow', + name: 'netflow.mib_module_name', + type: 'keyword', + }, + 'netflow.mobile_imsi': { + category: 'netflow', + name: 'netflow.mobile_imsi', + type: 'keyword', + }, + 'netflow.mobile_msisdn': { + category: 'netflow', + name: 'netflow.mobile_msisdn', + type: 'keyword', + }, + 'netflow.http_status_code': { + category: 'netflow', + name: 'netflow.http_status_code', + type: 'integer', + }, + 'netflow.source_transport_ports_limit': { + category: 'netflow', + name: 'netflow.source_transport_ports_limit', + type: 'integer', + }, + 'netflow.http_request_method': { + category: 'netflow', + name: 'netflow.http_request_method', + type: 'keyword', + }, + 'netflow.http_request_host': { + category: 'netflow', + name: 'netflow.http_request_host', + type: 'keyword', + }, + 'netflow.http_request_target': { + category: 'netflow', + name: 'netflow.http_request_target', + type: 'keyword', + }, + 'netflow.http_message_version': { + category: 'netflow', + name: 'netflow.http_message_version', + type: 'keyword', + }, + 'netflow.nat_instance_id': { + category: 'netflow', + name: 'netflow.nat_instance_id', + type: 'long', + }, + 'netflow.internal_address_realm': { + category: 'netflow', + name: 'netflow.internal_address_realm', + type: 'short', + }, + 'netflow.external_address_realm': { + category: 'netflow', + name: 'netflow.external_address_realm', + type: 'short', + }, + 'netflow.nat_quota_exceeded_event': { + category: 'netflow', + name: 'netflow.nat_quota_exceeded_event', + type: 'long', + }, + 'netflow.nat_threshold_event': { + category: 'netflow', + name: 'netflow.nat_threshold_event', + type: 'long', + }, + 'netflow.http_user_agent': { + category: 'netflow', + name: 'netflow.http_user_agent', + type: 'keyword', + }, + 'netflow.http_content_type': { + category: 'netflow', + name: 'netflow.http_content_type', + type: 'keyword', + }, + 'netflow.http_reason_phrase': { + category: 'netflow', + name: 'netflow.http_reason_phrase', + type: 'keyword', + }, + 'netflow.max_session_entries': { + category: 'netflow', + name: 'netflow.max_session_entries', + type: 'long', + }, + 'netflow.max_bib_entries': { + category: 'netflow', + name: 'netflow.max_bib_entries', + type: 'long', + }, + 'netflow.max_entries_per_user': { + category: 'netflow', + name: 'netflow.max_entries_per_user', + type: 'long', + }, + 'netflow.max_subscribers': { + category: 'netflow', + name: 'netflow.max_subscribers', + type: 'long', + }, + 'netflow.max_fragments_pending_reassembly': { + category: 'netflow', + name: 'netflow.max_fragments_pending_reassembly', + type: 'long', + }, + 'netflow.address_pool_high_threshold': { + category: 'netflow', + name: 'netflow.address_pool_high_threshold', + type: 'long', + }, + 'netflow.address_pool_low_threshold': { + category: 'netflow', + name: 'netflow.address_pool_low_threshold', + type: 'long', + }, + 'netflow.address_port_mapping_high_threshold': { + category: 'netflow', + name: 'netflow.address_port_mapping_high_threshold', + type: 'long', + }, + 'netflow.address_port_mapping_low_threshold': { + category: 'netflow', + name: 'netflow.address_port_mapping_low_threshold', + type: 'long', + }, + 'netflow.address_port_mapping_per_user_high_threshold': { + category: 'netflow', + name: 'netflow.address_port_mapping_per_user_high_threshold', + type: 'long', + }, + 'netflow.global_address_mapping_high_threshold': { + category: 'netflow', + name: 'netflow.global_address_mapping_high_threshold', + type: 'long', + }, + 'netflow.vpn_identifier': { + category: 'netflow', + name: 'netflow.vpn_identifier', + type: 'short', + }, + bucket_name: { + category: 'base', + description: 'Name of the S3 bucket that this log retrieved from. ', + name: 'bucket_name', + type: 'keyword', + }, + object_key: { + category: 'base', + description: 'Name of the S3 object that this log retrieved from. ', + name: 'object_key', + type: 'keyword', + }, + 'cef.version': { + category: 'cef', + description: 'Version of the CEF specification used by the message. ', + name: 'cef.version', + type: 'keyword', + }, + 'cef.device.vendor': { + category: 'cef', + description: 'Vendor of the device that produced the message. ', + name: 'cef.device.vendor', + type: 'keyword', + }, + 'cef.device.product': { + category: 'cef', + description: 'Product of the device that produced the message. ', + name: 'cef.device.product', + type: 'keyword', + }, + 'cef.device.version': { + category: 'cef', + description: 'Version of the product that produced the message. ', + name: 'cef.device.version', + type: 'keyword', + }, + 'cef.device.event_class_id': { + category: 'cef', + description: 'Unique identifier of the event type. ', + name: 'cef.device.event_class_id', + type: 'keyword', + }, + 'cef.severity': { + category: 'cef', + description: + 'Importance of the event. The valid string values are Unknown, Low, Medium, High, and Very-High. The valid integer values are 0-3=Low, 4-6=Medium, 7- 8=High, and 9-10=Very-High. ', + example: 'Very-High', + name: 'cef.severity', + type: 'keyword', + }, + 'cef.name': { + category: 'cef', + description: 'Short description of the event. ', + name: 'cef.name', + type: 'keyword', + }, + 'cef.extensions.agentAddress': { + category: 'cef', + description: 'The IP address of the ArcSight connector that processed the event.', + name: 'cef.extensions.agentAddress', + type: 'ip', + }, + 'cef.extensions.agentDnsDomain': { + category: 'cef', + description: 'The DNS domain name of the ArcSight connector that processed the event.', + name: 'cef.extensions.agentDnsDomain', + type: 'keyword', + }, + 'cef.extensions.agentHostName': { + category: 'cef', + description: 'The hostname of the ArcSight connector that processed the event.', + name: 'cef.extensions.agentHostName', + type: 'keyword', + }, + 'cef.extensions.agentId': { + category: 'cef', + description: 'The agent ID of the ArcSight connector that processed the event.', + name: 'cef.extensions.agentId', + type: 'keyword', + }, + 'cef.extensions.agentMacAddress': { + category: 'cef', + description: 'The MAC address of the ArcSight connector that processed the event.', + name: 'cef.extensions.agentMacAddress', + type: 'keyword', + }, + 'cef.extensions.agentNtDomain': { + category: 'cef', + description: 'null', + name: 'cef.extensions.agentNtDomain', + type: 'keyword', + }, + 'cef.extensions.agentReceiptTime': { + category: 'cef', + description: + 'The time at which information about the event was received by the ArcSight connector.', + name: 'cef.extensions.agentReceiptTime', + type: 'date', + }, + 'cef.extensions.agentTimeZone': { + category: 'cef', + description: 'The agent time zone of the ArcSight connector that processed the event.', + name: 'cef.extensions.agentTimeZone', + type: 'keyword', + }, + 'cef.extensions.agentTranslatedAddress': { + category: 'cef', + description: 'null', + name: 'cef.extensions.agentTranslatedAddress', + type: 'ip', + }, + 'cef.extensions.agentTranslatedZoneExternalID': { + category: 'cef', + description: 'null', + name: 'cef.extensions.agentTranslatedZoneExternalID', + type: 'keyword', + }, + 'cef.extensions.agentTranslatedZoneURI': { + category: 'cef', + description: 'null', + name: 'cef.extensions.agentTranslatedZoneURI', + type: 'keyword', + }, + 'cef.extensions.agentType': { + category: 'cef', + description: 'The agent type of the ArcSight connector that processed the event', + name: 'cef.extensions.agentType', + type: 'keyword', + }, + 'cef.extensions.agentVersion': { + category: 'cef', + description: 'The version of the ArcSight connector that processed the event.', + name: 'cef.extensions.agentVersion', + type: 'keyword', + }, + 'cef.extensions.agentZoneExternalID': { + category: 'cef', + description: 'null', + name: 'cef.extensions.agentZoneExternalID', + type: 'keyword', + }, + 'cef.extensions.agentZoneURI': { + category: 'cef', + description: 'null', + name: 'cef.extensions.agentZoneURI', + type: 'keyword', + }, + 'cef.extensions.applicationProtocol': { + category: 'cef', + description: + 'Application level protocol, example values are HTTP, HTTPS, SSHv2, Telnet, POP, IMPA, IMAPS, and so on.', + name: 'cef.extensions.applicationProtocol', + type: 'keyword', + }, + 'cef.extensions.baseEventCount': { + category: 'cef', + description: + 'A count associated with this event. How many times was this same event observed? Count can be omitted if it is 1.', + name: 'cef.extensions.baseEventCount', + type: 'long', + }, + 'cef.extensions.bytesIn': { + category: 'cef', + description: + 'Number of bytes transferred inbound, relative to the source to destination relationship, meaning that data was flowing from source to destination.', + name: 'cef.extensions.bytesIn', + type: 'long', + }, + 'cef.extensions.bytesOut': { + category: 'cef', + description: + 'Number of bytes transferred outbound relative to the source to destination relationship. For example, the byte number of data flowing from the destination to the source.', + name: 'cef.extensions.bytesOut', + type: 'long', + }, + 'cef.extensions.customerExternalID': { + category: 'cef', + description: 'null', + name: 'cef.extensions.customerExternalID', + type: 'keyword', + }, + 'cef.extensions.customerURI': { + category: 'cef', + description: 'null', + name: 'cef.extensions.customerURI', + type: 'keyword', + }, + 'cef.extensions.destinationAddress': { + category: 'cef', + description: + 'Identifies the destination address that the event refers to in an IP network. The format is an IPv4 address.', + name: 'cef.extensions.destinationAddress', + type: 'ip', + }, + 'cef.extensions.destinationDnsDomain': { + category: 'cef', + description: 'The DNS domain part of the complete fully qualified domain name (FQDN).', + name: 'cef.extensions.destinationDnsDomain', + type: 'keyword', + }, + 'cef.extensions.destinationGeoLatitude': { + category: 'cef', + description: "The latitudinal value from which the destination's IP address belongs.", + name: 'cef.extensions.destinationGeoLatitude', + type: 'double', + }, + 'cef.extensions.destinationGeoLongitude': { + category: 'cef', + description: "The longitudinal value from which the destination's IP address belongs.", + name: 'cef.extensions.destinationGeoLongitude', + type: 'double', + }, + 'cef.extensions.destinationHostName': { + category: 'cef', + description: + 'Identifies the destination that an event refers to in an IP network. The format should be a fully qualified domain name (FQDN) associated with the destination node, when a node is available.', + name: 'cef.extensions.destinationHostName', + type: 'keyword', + }, + 'cef.extensions.destinationMacAddress': { + category: 'cef', + description: 'Six colon-seperated hexadecimal numbers.', + name: 'cef.extensions.destinationMacAddress', + type: 'keyword', + }, + 'cef.extensions.destinationNtDomain': { + category: 'cef', + description: 'The Windows domain name of the destination address.', + name: 'cef.extensions.destinationNtDomain', + type: 'keyword', + }, + 'cef.extensions.destinationPort': { + category: 'cef', + description: 'The valid port numbers are between 0 and 65535.', + name: 'cef.extensions.destinationPort', + type: 'long', + }, + 'cef.extensions.destinationProcessId': { + category: 'cef', + description: + 'Provides the ID of the destination process associated with the event. For example, if an event contains process ID 105, "105" is the process ID.', + name: 'cef.extensions.destinationProcessId', + type: 'long', + }, + 'cef.extensions.destinationProcessName': { + category: 'cef', + description: "The name of the event's destination process.", + name: 'cef.extensions.destinationProcessName', + type: 'keyword', + }, + 'cef.extensions.destinationServiceName': { + category: 'cef', + description: 'The service targeted by this event.', + name: 'cef.extensions.destinationServiceName', + type: 'keyword', + }, + 'cef.extensions.destinationTranslatedAddress': { + category: 'cef', + description: 'Identifies the translated destination that the event refers to in an IP network.', + name: 'cef.extensions.destinationTranslatedAddress', + type: 'ip', + }, + 'cef.extensions.destinationTranslatedPort': { + category: 'cef', + description: + 'Port after it was translated; for example, a firewall. Valid port numbers are 0 to 65535.', + name: 'cef.extensions.destinationTranslatedPort', + type: 'long', + }, + 'cef.extensions.destinationTranslatedZoneExternalID': { + category: 'cef', + description: 'null', + name: 'cef.extensions.destinationTranslatedZoneExternalID', + type: 'keyword', + }, + 'cef.extensions.destinationTranslatedZoneURI': { + category: 'cef', + description: + 'The URI for the Translated Zone that the destination asset has been assigned to in ArcSight.', + name: 'cef.extensions.destinationTranslatedZoneURI', + type: 'keyword', + }, + 'cef.extensions.destinationUserId': { + category: 'cef', + description: + 'Identifies the destination user by ID. For example, in UNIX, the root user is generally associated with user ID 0.', + name: 'cef.extensions.destinationUserId', + type: 'keyword', + }, + 'cef.extensions.destinationUserName': { + category: 'cef', + description: + "Identifies the destination user by name. This is the user associated with the event's destination. Email addresses are often mapped into the UserName fields. The recipient is a candidate to put into this field.", + name: 'cef.extensions.destinationUserName', + type: 'keyword', + }, + 'cef.extensions.destinationUserPrivileges': { + category: 'cef', + description: + 'The typical values are "Administrator", "User", and "Guest". This identifies the destination user\'s privileges. In UNIX, for example, activity executed on the root user would be identified with destinationUser Privileges of "Administrator".', + name: 'cef.extensions.destinationUserPrivileges', + type: 'keyword', + }, + 'cef.extensions.destinationZoneExternalID': { + category: 'cef', + description: 'null', + name: 'cef.extensions.destinationZoneExternalID', + type: 'keyword', + }, + 'cef.extensions.destinationZoneURI': { + category: 'cef', + description: + 'The URI for the Zone that the destination asset has been assigned to in ArcSight.', + name: 'cef.extensions.destinationZoneURI', + type: 'keyword', + }, + 'cef.extensions.deviceAction': { + category: 'cef', + description: 'Action taken by the device.', + name: 'cef.extensions.deviceAction', + type: 'keyword', + }, + 'cef.extensions.deviceAddress': { + category: 'cef', + description: 'Identifies the device address that an event refers to in an IP network.', + name: 'cef.extensions.deviceAddress', + type: 'ip', + }, + 'cef.extensions.deviceCustomFloatingPoint1Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomFloatingPoint1Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomFloatingPoint3Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomFloatingPoint3Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomFloatingPoint4Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomFloatingPoint4Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomDate1': { + category: 'cef', + description: + 'One of two timestamp fields available to map fields that do not apply to any other in this dictionary.', + name: 'cef.extensions.deviceCustomDate1', + type: 'date', + }, + 'cef.extensions.deviceCustomDate1Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomDate1Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomDate2': { + category: 'cef', + description: + 'One of two timestamp fields available to map fields that do not apply to any other in this dictionary.', + name: 'cef.extensions.deviceCustomDate2', + type: 'date', + }, + 'cef.extensions.deviceCustomDate2Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomDate2Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomFloatingPoint1': { + category: 'cef', + description: + 'One of four floating point fields available to map fields that do not apply to any other in this dictionary.', + name: 'cef.extensions.deviceCustomFloatingPoint1', + type: 'double', + }, + 'cef.extensions.deviceCustomFloatingPoint2': { + category: 'cef', + description: + 'One of four floating point fields available to map fields that do not apply to any other in this dictionary.', + name: 'cef.extensions.deviceCustomFloatingPoint2', + type: 'double', + }, + 'cef.extensions.deviceCustomFloatingPoint2Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomFloatingPoint2Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomFloatingPoint3': { + category: 'cef', + description: + 'One of four floating point fields available to map fields that do not apply to any other in this dictionary.', + name: 'cef.extensions.deviceCustomFloatingPoint3', + type: 'double', + }, + 'cef.extensions.deviceCustomFloatingPoint4': { + category: 'cef', + description: + 'One of four floating point fields available to map fields that do not apply to any other in this dictionary.', + name: 'cef.extensions.deviceCustomFloatingPoint4', + type: 'double', + }, + 'cef.extensions.deviceCustomIPv6Address1': { + category: 'cef', + description: + 'One of four IPv6 address fields available to map fields that do not apply to any other in this dictionary.', + name: 'cef.extensions.deviceCustomIPv6Address1', + type: 'ip', + }, + 'cef.extensions.deviceCustomIPv6Address1Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomIPv6Address1Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomIPv6Address2': { + category: 'cef', + description: + 'One of four IPv6 address fields available to map fields that do not apply to any other in this dictionary.', + name: 'cef.extensions.deviceCustomIPv6Address2', + type: 'ip', + }, + 'cef.extensions.deviceCustomIPv6Address2Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomIPv6Address2Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomIPv6Address3': { + category: 'cef', + description: + 'One of four IPv6 address fields available to map fields that do not apply to any other in this dictionary.', + name: 'cef.extensions.deviceCustomIPv6Address3', + type: 'ip', + }, + 'cef.extensions.deviceCustomIPv6Address3Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomIPv6Address3Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomIPv6Address4': { + category: 'cef', + description: + 'One of four IPv6 address fields available to map fields that do not apply to any other in this dictionary.', + name: 'cef.extensions.deviceCustomIPv6Address4', + type: 'ip', + }, + 'cef.extensions.deviceCustomIPv6Address4Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomIPv6Address4Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomNumber1': { + category: 'cef', + description: + 'One of three number fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', + name: 'cef.extensions.deviceCustomNumber1', + type: 'long', + }, + 'cef.extensions.deviceCustomNumber1Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomNumber1Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomNumber2': { + category: 'cef', + description: + 'One of three number fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', + name: 'cef.extensions.deviceCustomNumber2', + type: 'long', + }, + 'cef.extensions.deviceCustomNumber2Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomNumber2Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomNumber3': { + category: 'cef', + description: + 'One of three number fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', + name: 'cef.extensions.deviceCustomNumber3', + type: 'long', + }, + 'cef.extensions.deviceCustomNumber3Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomNumber3Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomString1': { + category: 'cef', + description: + 'One of six strings available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', + name: 'cef.extensions.deviceCustomString1', + type: 'keyword', + }, + 'cef.extensions.deviceCustomString1Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomString1Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomString2': { + category: 'cef', + description: + 'One of six strings available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', + name: 'cef.extensions.deviceCustomString2', + type: 'keyword', + }, + 'cef.extensions.deviceCustomString2Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomString2Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomString3': { + category: 'cef', + description: + 'One of six strings available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', + name: 'cef.extensions.deviceCustomString3', + type: 'keyword', + }, + 'cef.extensions.deviceCustomString3Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomString3Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomString4': { + category: 'cef', + description: + 'One of six strings available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', + name: 'cef.extensions.deviceCustomString4', + type: 'keyword', + }, + 'cef.extensions.deviceCustomString4Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomString4Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomString5': { + category: 'cef', + description: + 'One of six strings available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', + name: 'cef.extensions.deviceCustomString5', + type: 'keyword', + }, + 'cef.extensions.deviceCustomString5Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomString5Label', + type: 'keyword', + }, + 'cef.extensions.deviceCustomString6': { + category: 'cef', + description: + 'One of six strings available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', + name: 'cef.extensions.deviceCustomString6', + type: 'keyword', + }, + 'cef.extensions.deviceCustomString6Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceCustomString6Label', + type: 'keyword', + }, + 'cef.extensions.deviceDirection': { + category: 'cef', + description: + 'Any information about what direction the observed communication has taken. The following values are supported - "0" for inbound or "1" for outbound.', + name: 'cef.extensions.deviceDirection', + type: 'long', + }, + 'cef.extensions.deviceDnsDomain': { + category: 'cef', + description: 'The DNS domain part of the complete fully qualified domain name (FQDN).', + name: 'cef.extensions.deviceDnsDomain', + type: 'keyword', + }, + 'cef.extensions.deviceEventCategory': { + category: 'cef', + description: + 'Represents the category assigned by the originating device. Devices often use their own categorization schema to classify event. Example "/Monitor/Disk/Read".', + name: 'cef.extensions.deviceEventCategory', + type: 'keyword', + }, + 'cef.extensions.deviceExternalId': { + category: 'cef', + description: 'A name that uniquely identifies the device generating this event.', + name: 'cef.extensions.deviceExternalId', + type: 'keyword', + }, + 'cef.extensions.deviceFacility': { + category: 'cef', + description: + 'The facility generating this event. For example, Syslog has an explicit facility associated with every event.', + name: 'cef.extensions.deviceFacility', + type: 'keyword', + }, + 'cef.extensions.deviceFlexNumber1': { + category: 'cef', + description: + 'One of two alternative number fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', + name: 'cef.extensions.deviceFlexNumber1', + type: 'long', + }, + 'cef.extensions.deviceFlexNumber1Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceFlexNumber1Label', + type: 'keyword', + }, + 'cef.extensions.deviceFlexNumber2': { + category: 'cef', + description: + 'One of two alternative number fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible.', + name: 'cef.extensions.deviceFlexNumber2', + type: 'long', + }, + 'cef.extensions.deviceFlexNumber2Label': { + category: 'cef', + description: + 'All custom fields have a corresponding label field. Each of these fields is a string and describes the purpose of the custom field.', + name: 'cef.extensions.deviceFlexNumber2Label', + type: 'keyword', + }, + 'cef.extensions.deviceHostName': { + category: 'cef', + description: + 'The format should be a fully qualified domain name (FQDN) associated with the device node, when a node is available.', + name: 'cef.extensions.deviceHostName', + type: 'keyword', + }, + 'cef.extensions.deviceInboundInterface': { + category: 'cef', + description: 'Interface on which the packet or data entered the device.', + name: 'cef.extensions.deviceInboundInterface', + type: 'keyword', + }, + 'cef.extensions.deviceMacAddress': { + category: 'cef', + description: 'Six colon-separated hexadecimal numbers.', + name: 'cef.extensions.deviceMacAddress', + type: 'keyword', + }, + 'cef.extensions.deviceNtDomain': { + category: 'cef', + description: 'The Windows domain name of the device address.', + name: 'cef.extensions.deviceNtDomain', + type: 'keyword', + }, + 'cef.extensions.deviceOutboundInterface': { + category: 'cef', + description: 'Interface on which the packet or data left the device.', + name: 'cef.extensions.deviceOutboundInterface', + type: 'keyword', + }, + 'cef.extensions.devicePayloadId': { + category: 'cef', + description: 'Unique identifier for the payload associated with the event.', + name: 'cef.extensions.devicePayloadId', + type: 'keyword', + }, + 'cef.extensions.deviceProcessId': { + category: 'cef', + description: 'Provides the ID of the process on the device generating the event.', + name: 'cef.extensions.deviceProcessId', + type: 'long', + }, + 'cef.extensions.deviceProcessName': { + category: 'cef', + description: + 'Process name associated with the event. An example might be the process generating the syslog entry in UNIX.', + name: 'cef.extensions.deviceProcessName', + type: 'keyword', + }, + 'cef.extensions.deviceReceiptTime': { + category: 'cef', + description: + 'The time at which the event related to the activity was received. The format is MMM dd yyyy HH:mm:ss or milliseconds since epoch (Jan 1st 1970)', + name: 'cef.extensions.deviceReceiptTime', + type: 'date', + }, + 'cef.extensions.deviceTimeZone': { + category: 'cef', + description: 'The time zone for the device generating the event.', + name: 'cef.extensions.deviceTimeZone', + type: 'keyword', + }, + 'cef.extensions.deviceTranslatedAddress': { + category: 'cef', + description: + 'Identifies the translated device address that the event refers to in an IP network.', + name: 'cef.extensions.deviceTranslatedAddress', + type: 'ip', + }, + 'cef.extensions.deviceTranslatedZoneExternalID': { + category: 'cef', + description: 'null', + name: 'cef.extensions.deviceTranslatedZoneExternalID', + type: 'keyword', + }, + 'cef.extensions.deviceTranslatedZoneURI': { + category: 'cef', + description: + 'The URI for the Translated Zone that the device asset has been assigned to in ArcSight.', + name: 'cef.extensions.deviceTranslatedZoneURI', + type: 'keyword', + }, + 'cef.extensions.deviceZoneExternalID': { + category: 'cef', + description: 'null', + name: 'cef.extensions.deviceZoneExternalID', + type: 'keyword', + }, + 'cef.extensions.deviceZoneURI': { + category: 'cef', + description: 'Thee URI for the Zone that the device asset has been assigned to in ArcSight.', + name: 'cef.extensions.deviceZoneURI', + type: 'keyword', + }, + 'cef.extensions.endTime': { + category: 'cef', + description: + 'The time at which the activity related to the event ended. The format is MMM dd yyyy HH:mm:ss or milliseconds since epoch (Jan 1st1970). An example would be reporting the end of a session.', + name: 'cef.extensions.endTime', + type: 'date', + }, + 'cef.extensions.eventId': { + category: 'cef', + description: 'This is a unique ID that ArcSight assigns to each event.', + name: 'cef.extensions.eventId', + type: 'long', + }, + 'cef.extensions.eventOutcome': { + category: 'cef', + description: "Displays the outcome, usually as 'success' or 'failure'.", + name: 'cef.extensions.eventOutcome', + type: 'keyword', + }, + 'cef.extensions.externalId': { + category: 'cef', + description: + 'The ID used by an originating device. They are usually increasing numbers, associated with events.', + name: 'cef.extensions.externalId', + type: 'keyword', + }, + 'cef.extensions.fileCreateTime': { + category: 'cef', + description: 'Time when the file was created.', + name: 'cef.extensions.fileCreateTime', + type: 'date', + }, + 'cef.extensions.fileHash': { + category: 'cef', + description: 'Hash of a file.', + name: 'cef.extensions.fileHash', + type: 'keyword', + }, + 'cef.extensions.fileId': { + category: 'cef', + description: 'An ID associated with a file could be the inode.', + name: 'cef.extensions.fileId', + type: 'keyword', + }, + 'cef.extensions.fileModificationTime': { + category: 'cef', + description: 'Time when the file was last modified.', + name: 'cef.extensions.fileModificationTime', + type: 'date', + }, + 'cef.extensions.filename': { + category: 'cef', + description: 'Name of the file only (without its path).', + name: 'cef.extensions.filename', + type: 'keyword', + }, + 'cef.extensions.filePath': { + category: 'cef', + description: 'Full path to the file, including file name itself.', + name: 'cef.extensions.filePath', + type: 'keyword', + }, + 'cef.extensions.filePermission': { + category: 'cef', + description: 'Permissions of the file.', + name: 'cef.extensions.filePermission', + type: 'keyword', + }, + 'cef.extensions.fileSize': { + category: 'cef', + description: 'Size of the file.', + name: 'cef.extensions.fileSize', + type: 'long', + }, + 'cef.extensions.fileType': { + category: 'cef', + description: 'Type of file (pipe, socket, etc.)', + name: 'cef.extensions.fileType', + type: 'keyword', + }, + 'cef.extensions.flexDate1': { + category: 'cef', + description: + 'A timestamp field available to map a timestamp that does not apply to any other defined timestamp field in this dictionary. Use all flex fields sparingly and seek a more specific, dictionary supplied field when possible. These fields are typically reserved for customer use and should not be set by vendors unless necessary.', + name: 'cef.extensions.flexDate1', + type: 'date', + }, + 'cef.extensions.flexDate1Label': { + category: 'cef', + description: 'The label field is a string and describes the purpose of the flex field.', + name: 'cef.extensions.flexDate1Label', + type: 'keyword', + }, + 'cef.extensions.flexString1': { + category: 'cef', + description: + 'One of four floating point fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible. These fields are typically reserved for customer use and should not be set by vendors unless necessary.', + name: 'cef.extensions.flexString1', + type: 'keyword', + }, + 'cef.extensions.flexString2': { + category: 'cef', + description: + 'One of four floating point fields available to map fields that do not apply to any other in this dictionary. Use sparingly and seek a more specific, dictionary supplied field when possible. These fields are typically reserved for customer use and should not be set by vendors unless necessary.', + name: 'cef.extensions.flexString2', + type: 'keyword', + }, + 'cef.extensions.flexString1Label': { + category: 'cef', + description: 'The label field is a string and describes the purpose of the flex field.', + name: 'cef.extensions.flexString1Label', + type: 'keyword', + }, + 'cef.extensions.flexString2Label': { + category: 'cef', + description: 'The label field is a string and describes the purpose of the flex field.', + name: 'cef.extensions.flexString2Label', + type: 'keyword', + }, + 'cef.extensions.message': { + category: 'cef', + description: + 'An arbitrary message giving more details about the event. Multi-line entries can be produced by using \\n as the new line separator.', + name: 'cef.extensions.message', + type: 'keyword', + }, + 'cef.extensions.oldFileCreateTime': { + category: 'cef', + description: 'Time when old file was created.', + name: 'cef.extensions.oldFileCreateTime', + type: 'date', + }, + 'cef.extensions.oldFileHash': { + category: 'cef', + description: 'Hash of the old file.', + name: 'cef.extensions.oldFileHash', + type: 'keyword', + }, + 'cef.extensions.oldFileId': { + category: 'cef', + description: 'An ID associated with the old file could be the inode.', + name: 'cef.extensions.oldFileId', + type: 'keyword', + }, + 'cef.extensions.oldFileModificationTime': { + category: 'cef', + description: 'Time when old file was last modified.', + name: 'cef.extensions.oldFileModificationTime', + type: 'date', + }, + 'cef.extensions.oldFileName': { + category: 'cef', + description: 'Name of the old file.', + name: 'cef.extensions.oldFileName', + type: 'keyword', + }, + 'cef.extensions.oldFilePath': { + category: 'cef', + description: 'Full path to the old file, including the file name itself.', + name: 'cef.extensions.oldFilePath', + type: 'keyword', + }, + 'cef.extensions.oldFilePermission': { + category: 'cef', + description: 'Permissions of the old file.', + name: 'cef.extensions.oldFilePermission', + type: 'keyword', + }, + 'cef.extensions.oldFileSize': { + category: 'cef', + description: 'Size of the old file.', + name: 'cef.extensions.oldFileSize', + type: 'long', + }, + 'cef.extensions.oldFileType': { + category: 'cef', + description: 'Type of the old file (pipe, socket, etc.)', + name: 'cef.extensions.oldFileType', + type: 'keyword', + }, + 'cef.extensions.rawEvent': { + category: 'cef', + description: 'null', + name: 'cef.extensions.rawEvent', + type: 'keyword', + }, + 'cef.extensions.Reason': { + category: 'cef', + description: + 'The reason an audit event was generated. For example "bad password" or "unknown user". This could also be an error or return code. Example "0x1234".', + name: 'cef.extensions.Reason', + type: 'keyword', + }, + 'cef.extensions.requestClientApplication': { + category: 'cef', + description: 'The User-Agent associated with the request.', + name: 'cef.extensions.requestClientApplication', + type: 'keyword', + }, + 'cef.extensions.requestContext': { + category: 'cef', + description: + 'Description of the content from which the request originated (for example, HTTP Referrer)', + name: 'cef.extensions.requestContext', + type: 'keyword', + }, + 'cef.extensions.requestCookies': { + category: 'cef', + description: 'Cookies associated with the request.', + name: 'cef.extensions.requestCookies', + type: 'keyword', + }, + 'cef.extensions.requestMethod': { + category: 'cef', + description: 'The HTTP method used to access a URL.', + name: 'cef.extensions.requestMethod', + type: 'keyword', + }, + 'cef.extensions.requestUrl': { + category: 'cef', + description: + 'In the case of an HTTP request, this field contains the URL accessed. The URL should contain the protocol as well.', + name: 'cef.extensions.requestUrl', + type: 'keyword', + }, + 'cef.extensions.sourceAddress': { + category: 'cef', + description: 'Identifies the source that an event refers to in an IP network.', + name: 'cef.extensions.sourceAddress', + type: 'ip', + }, + 'cef.extensions.sourceDnsDomain': { + category: 'cef', + description: 'The DNS domain part of the complete fully qualified domain name (FQDN).', + name: 'cef.extensions.sourceDnsDomain', + type: 'keyword', + }, + 'cef.extensions.sourceGeoLatitude': { + category: 'cef', + description: 'null', + name: 'cef.extensions.sourceGeoLatitude', + type: 'double', + }, + 'cef.extensions.sourceGeoLongitude': { + category: 'cef', + description: 'null', + name: 'cef.extensions.sourceGeoLongitude', + type: 'double', + }, + 'cef.extensions.sourceHostName': { + category: 'cef', + description: + "Identifies the source that an event refers to in an IP network. The format should be a fully qualified domain name (FQDN) associated with the source node, when a mode is available. Examples: 'host' or 'host.domain.com'. ", + name: 'cef.extensions.sourceHostName', + type: 'keyword', + }, + 'cef.extensions.sourceMacAddress': { + category: 'cef', + description: 'Six colon-separated hexadecimal numbers.', + example: '00:0d:60:af:1b:61', + name: 'cef.extensions.sourceMacAddress', + type: 'keyword', + }, + 'cef.extensions.sourceNtDomain': { + category: 'cef', + description: 'The Windows domain name for the source address.', + name: 'cef.extensions.sourceNtDomain', + type: 'keyword', + }, + 'cef.extensions.sourcePort': { + category: 'cef', + description: 'The valid port numbers are 0 to 65535.', + name: 'cef.extensions.sourcePort', + type: 'long', + }, + 'cef.extensions.sourceProcessId': { + category: 'cef', + description: 'The ID of the source process associated with the event.', + name: 'cef.extensions.sourceProcessId', + type: 'long', + }, + 'cef.extensions.sourceProcessName': { + category: 'cef', + description: "The name of the event's source process.", + name: 'cef.extensions.sourceProcessName', + type: 'keyword', + }, + 'cef.extensions.sourceServiceName': { + category: 'cef', + description: 'The service that is responsible for generating this event.', + name: 'cef.extensions.sourceServiceName', + type: 'keyword', + }, + 'cef.extensions.sourceTranslatedAddress': { + category: 'cef', + description: 'Identifies the translated source that the event refers to in an IP network.', + name: 'cef.extensions.sourceTranslatedAddress', + type: 'ip', + }, + 'cef.extensions.sourceTranslatedPort': { + category: 'cef', + description: + 'A port number after being translated by, for example, a firewall. Valid port numbers are 0 to 65535.', + name: 'cef.extensions.sourceTranslatedPort', + type: 'long', + }, + 'cef.extensions.sourceTranslatedZoneExternalID': { + category: 'cef', + description: 'null', + name: 'cef.extensions.sourceTranslatedZoneExternalID', + type: 'keyword', + }, + 'cef.extensions.sourceTranslatedZoneURI': { + category: 'cef', + description: + 'The URI for the Translated Zone that the destination asset has been assigned to in ArcSight.', + name: 'cef.extensions.sourceTranslatedZoneURI', + type: 'keyword', + }, + 'cef.extensions.sourceUserId': { + category: 'cef', + description: + 'Identifies the source user by ID. This is the user associated with the source of the event. For example, in UNIX, the root user is generally associated with user ID 0.', + name: 'cef.extensions.sourceUserId', + type: 'keyword', + }, + 'cef.extensions.sourceUserName': { + category: 'cef', + description: + 'Identifies the source user by name. Email addresses are also mapped into the UserName fields. The sender is a candidate to put into this field.', + name: 'cef.extensions.sourceUserName', + type: 'keyword', + }, + 'cef.extensions.sourceUserPrivileges': { + category: 'cef', + description: + 'The typical values are "Administrator", "User", and "Guest". It identifies the source user\'s privileges. In UNIX, for example, activity executed by the root user would be identified with "Administrator".', + name: 'cef.extensions.sourceUserPrivileges', + type: 'keyword', + }, + 'cef.extensions.sourceZoneExternalID': { + category: 'cef', + description: 'null', + name: 'cef.extensions.sourceZoneExternalID', + type: 'keyword', + }, + 'cef.extensions.sourceZoneURI': { + category: 'cef', + description: 'The URI for the Zone that the source asset has been assigned to in ArcSight.', + name: 'cef.extensions.sourceZoneURI', + type: 'keyword', + }, + 'cef.extensions.startTime': { + category: 'cef', + description: + 'The time when the activity the event referred to started. The format is MMM dd yyyy HH:mm:ss or milliseconds since epoch (Jan 1st 1970)', + name: 'cef.extensions.startTime', + type: 'date', + }, + 'cef.extensions.transportProtocol': { + category: 'cef', + description: + 'Identifies the Layer-4 protocol used. The possible values are protocols such as TCP or UDP.', + name: 'cef.extensions.transportProtocol', + type: 'keyword', + }, + 'cef.extensions.type': { + category: 'cef', + description: + '0 means base event, 1 means aggregated, 2 means correlation, and 3 means action. This field can be omitted for base events (type 0).', + name: 'cef.extensions.type', + type: 'long', + }, + 'cef.extensions.categoryDeviceType': { + category: 'cef', + description: 'Device type. Examples - Proxy, IDS, Web Server', + name: 'cef.extensions.categoryDeviceType', + type: 'keyword', + }, + 'cef.extensions.categoryObject': { + category: 'cef', + description: + 'Object that the event is about. For example it can be an operating sytem, database, file, etc.', + name: 'cef.extensions.categoryObject', + type: 'keyword', + }, + 'cef.extensions.categoryBehavior': { + category: 'cef', + description: + "Action or a behavior associated with an event. It's what is being done to the object.", + name: 'cef.extensions.categoryBehavior', + type: 'keyword', + }, + 'cef.extensions.categoryTechnique': { + category: 'cef', + description: 'Technique being used (e.g. /DoS).', + name: 'cef.extensions.categoryTechnique', + type: 'keyword', + }, + 'cef.extensions.categoryDeviceGroup': { + category: 'cef', + description: 'General device group like Firewall.', + name: 'cef.extensions.categoryDeviceGroup', + type: 'keyword', + }, + 'cef.extensions.categorySignificance': { + category: 'cef', + description: 'Characterization of the importance of the event.', + name: 'cef.extensions.categorySignificance', + type: 'keyword', + }, + 'cef.extensions.categoryOutcome': { + category: 'cef', + description: 'Outcome of the event (e.g. sucess, failure, or attempt).', + name: 'cef.extensions.categoryOutcome', + type: 'keyword', + }, + 'cef.extensions.managerReceiptTime': { + category: 'cef', + description: 'When the Arcsight ESM received the event.', + name: 'cef.extensions.managerReceiptTime', + type: 'date', + }, + 'source.service.name': { + category: 'source', + description: 'Service that is the source of the event.', + name: 'source.service.name', + type: 'keyword', + }, + 'destination.service.name': { + category: 'destination', + description: 'Service that is the target of the event.', + name: 'destination.service.name', + type: 'keyword', + }, + type: { + category: 'base', + description: + 'The type of the transaction (for example, HTTP, MySQL, Redis, or RUM) or "flow" in case of flows. ', + name: 'type', + }, + 'server.process.name': { + category: 'server', + description: 'The name of the process that served the transaction. ', + name: 'server.process.name', + }, + 'server.process.args': { + category: 'server', + description: 'The command-line of the process that served the transaction. ', + name: 'server.process.args', + }, + 'server.process.executable': { + category: 'server', + description: 'Absolute path to the server process executable. ', + name: 'server.process.executable', + }, + 'server.process.working_directory': { + category: 'server', + description: 'The working directory of the server process. ', + name: 'server.process.working_directory', + }, + 'server.process.start': { + category: 'server', + description: 'The time the server process started. ', + name: 'server.process.start', + }, + 'client.process.name': { + category: 'client', + description: 'The name of the process that initiated the transaction. ', + name: 'client.process.name', + }, + 'client.process.args': { + category: 'client', + description: 'The command-line of the process that initiated the transaction. ', + name: 'client.process.args', + }, + 'client.process.executable': { + category: 'client', + description: 'Absolute path to the client process executable. ', + name: 'client.process.executable', + }, + 'client.process.working_directory': { + category: 'client', + description: 'The working directory of the client process. ', + name: 'client.process.working_directory', + }, + 'client.process.start': { + category: 'client', + description: 'The time the client process started. ', + name: 'client.process.start', + }, + real_ip: { + category: 'base', + description: + 'If the server initiating the transaction is a proxy, this field contains the original client IP address. For HTTP, for example, the IP address extracted from a configurable HTTP header, by default `X-Forwarded-For`. Unless this field is disabled, it always has a value, and it matches the `client_ip` for non proxy clients. ', + name: 'real_ip', + type: 'alias', + }, + transport: { + category: 'base', + description: + 'The transport protocol used for the transaction. If not specified, then tcp is assumed. ', + name: 'transport', + type: 'alias', + }, + 'flow.final': { + category: 'flow', + description: + 'Indicates if event is last event in flow. If final is false, the event reports an intermediate flow state only. ', + name: 'flow.final', + type: 'boolean', + }, + 'flow.id': { + category: 'flow', + description: 'Internal flow ID based on connection meta data and address. ', + name: 'flow.id', + }, + 'flow.vlan': { + category: 'flow', + description: + "VLAN identifier from the 802.1q frame. In case of a multi-tagged frame this field will be an array with the outer tag's VLAN identifier listed first. ", + name: 'flow.vlan', + type: 'long', + }, + flow_id: { + category: 'base', + name: 'flow_id', + type: 'alias', + }, + final: { + category: 'base', + name: 'final', + type: 'alias', + }, + vlan: { + category: 'base', + name: 'vlan', + type: 'alias', + }, + 'source.stats.net_bytes_total': { + category: 'source', + name: 'source.stats.net_bytes_total', + type: 'alias', + }, + 'source.stats.net_packets_total': { + category: 'source', + name: 'source.stats.net_packets_total', + type: 'alias', + }, + 'dest.stats.net_bytes_total': { + category: 'dest', + name: 'dest.stats.net_bytes_total', + type: 'alias', + }, + 'dest.stats.net_packets_total': { + category: 'dest', + name: 'dest.stats.net_packets_total', + type: 'alias', + }, + status: { + category: 'base', + description: + 'The high level status of the transaction. The way to compute this value depends on the protocol, but the result has a meaning independent of the protocol. ', + name: 'status', + }, + method: { + category: 'base', + description: + 'The command/verb/method of the transaction. For HTTP, this is the method name (GET, POST, PUT, and so on), for SQL this is the verb (SELECT, UPDATE, DELETE, and so on). ', + name: 'method', + }, + resource: { + category: 'base', + description: + 'The logical resource that this transaction refers to. For HTTP, this is the URL path up to the last slash (/). For example, if the URL is `/users/1`, the resource is `/users`. For databases, the resource is typically the table name. The field is not filled for all transaction types. ', + name: 'resource', + }, + path: { + category: 'base', + description: + 'The path the transaction refers to. For HTTP, this is the URL. For SQL databases, this is the table name. For key-value stores, this is the key. ', + name: 'path', + }, + query: { + category: 'base', + description: + 'The query in a human readable format. For HTTP, it will typically be something like `GET /users/_search?name=test`. For MySQL, it is something like `SELECT id from users where name=test`. ', + name: 'query', + type: 'keyword', + }, + params: { + category: 'base', + description: + 'The request parameters. For HTTP, these are the POST or GET parameters. For Thrift-RPC, these are the parameters from the request. ', + name: 'params', + type: 'text', + }, + notes: { + category: 'base', + description: + 'Messages from Packetbeat itself. This field usually contains error messages for interpreting the raw data. This information can be helpful for troubleshooting. ', + name: 'notes', + type: 'alias', + }, + request: { + category: 'base', + description: + 'For text protocols, this is the request as seen on the wire (application layer only). For binary protocols this is our representation of the request. ', + name: 'request', + type: 'text', + }, + response: { + category: 'base', + description: + 'For text protocols, this is the response as seen on the wire (application layer only). For binary protocols this is our representation of the request. ', + name: 'response', + type: 'text', + }, + bytes_in: { + category: 'base', + description: + 'The number of bytes of the request. Note that this size is the application layer message length, without the length of the IP or TCP headers. ', + name: 'bytes_in', + type: 'alias', + }, + bytes_out: { + category: 'base', + description: + 'The number of bytes of the response. Note that this size is the application layer message length, without the length of the IP or TCP headers. ', + name: 'bytes_out', + type: 'alias', + }, + 'amqp.reply-code': { + category: 'amqp', + description: 'AMQP reply code to an error, similar to http reply-code ', + example: 404, + name: 'amqp.reply-code', + type: 'long', + }, + 'amqp.reply-text': { + category: 'amqp', + description: 'Text explaining the error. ', + name: 'amqp.reply-text', + type: 'keyword', + }, + 'amqp.class-id': { + category: 'amqp', + description: 'Failing method class. ', + name: 'amqp.class-id', + type: 'long', + }, + 'amqp.method-id': { + category: 'amqp', + description: 'Failing method ID. ', + name: 'amqp.method-id', + type: 'long', + }, + 'amqp.exchange': { + category: 'amqp', + description: 'Name of the exchange. ', + name: 'amqp.exchange', + type: 'keyword', + }, + 'amqp.exchange-type': { + category: 'amqp', + description: 'Exchange type. ', + example: 'fanout', + name: 'amqp.exchange-type', + type: 'keyword', + }, + 'amqp.passive': { + category: 'amqp', + description: 'If set, do not create exchange/queue. ', + name: 'amqp.passive', + type: 'boolean', + }, + 'amqp.durable': { + category: 'amqp', + description: 'If set, request a durable exchange/queue. ', + name: 'amqp.durable', + type: 'boolean', + }, + 'amqp.exclusive': { + category: 'amqp', + description: 'If set, request an exclusive queue. ', + name: 'amqp.exclusive', + type: 'boolean', + }, + 'amqp.auto-delete': { + category: 'amqp', + description: 'If set, auto-delete queue when unused. ', + name: 'amqp.auto-delete', + type: 'boolean', + }, + 'amqp.no-wait': { + category: 'amqp', + description: 'If set, the server will not respond to the method. ', + name: 'amqp.no-wait', + type: 'boolean', + }, + 'amqp.consumer-tag': { + category: 'amqp', + description: 'Identifier for the consumer, valid within the current channel. ', + name: 'amqp.consumer-tag', + }, + 'amqp.delivery-tag': { + category: 'amqp', + description: 'The server-assigned and channel-specific delivery tag. ', + name: 'amqp.delivery-tag', + type: 'long', + }, + 'amqp.message-count': { + category: 'amqp', + description: + 'The number of messages in the queue, which will be zero for newly-declared queues. ', + name: 'amqp.message-count', + type: 'long', + }, + 'amqp.consumer-count': { + category: 'amqp', + description: 'The number of consumers of a queue. ', + name: 'amqp.consumer-count', + type: 'long', + }, + 'amqp.routing-key': { + category: 'amqp', + description: 'Message routing key. ', + name: 'amqp.routing-key', + type: 'keyword', + }, + 'amqp.no-ack': { + category: 'amqp', + description: 'If set, the server does not expect acknowledgements for messages. ', + name: 'amqp.no-ack', + type: 'boolean', + }, + 'amqp.no-local': { + category: 'amqp', + description: + 'If set, the server will not send messages to the connection that published them. ', + name: 'amqp.no-local', + type: 'boolean', + }, + 'amqp.if-unused': { + category: 'amqp', + description: 'Delete only if unused. ', + name: 'amqp.if-unused', + type: 'boolean', + }, + 'amqp.if-empty': { + category: 'amqp', + description: 'Delete only if empty. ', + name: 'amqp.if-empty', + type: 'boolean', + }, + 'amqp.queue': { + category: 'amqp', + description: 'The queue name identifies the queue within the vhost. ', + name: 'amqp.queue', + type: 'keyword', + }, + 'amqp.redelivered': { + category: 'amqp', + description: + 'Indicates that the message has been previously delivered to this or another client. ', + name: 'amqp.redelivered', + type: 'boolean', + }, + 'amqp.multiple': { + category: 'amqp', + description: 'Acknowledge multiple messages. ', + name: 'amqp.multiple', + type: 'boolean', + }, + 'amqp.arguments': { + category: 'amqp', + description: 'Optional additional arguments passed to some methods. Can be of various types. ', + name: 'amqp.arguments', + type: 'object', + }, + 'amqp.mandatory': { + category: 'amqp', + description: 'Indicates mandatory routing. ', + name: 'amqp.mandatory', + type: 'boolean', + }, + 'amqp.immediate': { + category: 'amqp', + description: 'Request immediate delivery. ', + name: 'amqp.immediate', + type: 'boolean', + }, + 'amqp.content-type': { + category: 'amqp', + description: 'MIME content type. ', + example: 'text/plain', + name: 'amqp.content-type', + type: 'keyword', + }, + 'amqp.content-encoding': { + category: 'amqp', + description: 'MIME content encoding. ', + name: 'amqp.content-encoding', + type: 'keyword', + }, + 'amqp.headers': { + category: 'amqp', + description: 'Message header field table. ', + name: 'amqp.headers', + type: 'object', + }, + 'amqp.delivery-mode': { + category: 'amqp', + description: 'Non-persistent (1) or persistent (2). ', + name: 'amqp.delivery-mode', + type: 'keyword', + }, + 'amqp.priority': { + category: 'amqp', + description: 'Message priority, 0 to 9. ', + name: 'amqp.priority', + type: 'long', + }, + 'amqp.correlation-id': { + category: 'amqp', + description: 'Application correlation identifier. ', + name: 'amqp.correlation-id', + type: 'keyword', + }, + 'amqp.reply-to': { + category: 'amqp', + description: 'Address to reply to. ', + name: 'amqp.reply-to', + type: 'keyword', + }, + 'amqp.expiration': { + category: 'amqp', + description: 'Message expiration specification. ', + name: 'amqp.expiration', + type: 'keyword', + }, + 'amqp.message-id': { + category: 'amqp', + description: 'Application message identifier. ', + name: 'amqp.message-id', + type: 'keyword', + }, + 'amqp.timestamp': { + category: 'amqp', + description: 'Message timestamp. ', + name: 'amqp.timestamp', + type: 'keyword', + }, + 'amqp.type': { + category: 'amqp', + description: 'Message type name. ', + name: 'amqp.type', + type: 'keyword', + }, + 'amqp.user-id': { + category: 'amqp', + description: 'Creating user id. ', + name: 'amqp.user-id', + type: 'keyword', + }, + 'amqp.app-id': { + category: 'amqp', + description: 'Creating application id. ', + name: 'amqp.app-id', + type: 'keyword', + }, + no_request: { + category: 'base', + name: 'no_request', + type: 'alias', + }, + 'cassandra.no_request': { + category: 'cassandra', + description: 'Indicates that there is no request because this is a PUSH message. ', + name: 'cassandra.no_request', + type: 'boolean', + }, + 'cassandra.request.headers.version': { + category: 'cassandra', + description: 'The version of the protocol.', + name: 'cassandra.request.headers.version', + type: 'long', + }, + 'cassandra.request.headers.flags': { + category: 'cassandra', + description: 'Flags applying to this frame.', + name: 'cassandra.request.headers.flags', + type: 'keyword', + }, + 'cassandra.request.headers.stream': { + category: 'cassandra', + description: + 'A frame has a stream id. If a client sends a request message with the stream id X, it is guaranteed that the stream id of the response to that message will be X.', + name: 'cassandra.request.headers.stream', + type: 'keyword', + }, + 'cassandra.request.headers.op': { + category: 'cassandra', + description: 'An operation type that distinguishes the actual message.', + name: 'cassandra.request.headers.op', + type: 'keyword', + }, + 'cassandra.request.headers.length': { + category: 'cassandra', + description: + 'A integer representing the length of the body of the frame (a frame is limited to 256MB in length).', + name: 'cassandra.request.headers.length', + type: 'long', + }, + 'cassandra.request.query': { + category: 'cassandra', + description: 'The CQL query which client send to cassandra.', + name: 'cassandra.request.query', + type: 'keyword', + }, + 'cassandra.response.headers.version': { + category: 'cassandra', + description: 'The version of the protocol.', + name: 'cassandra.response.headers.version', + type: 'long', + }, + 'cassandra.response.headers.flags': { + category: 'cassandra', + description: 'Flags applying to this frame.', + name: 'cassandra.response.headers.flags', + type: 'keyword', + }, + 'cassandra.response.headers.stream': { + category: 'cassandra', + description: + 'A frame has a stream id. If a client sends a request message with the stream id X, it is guaranteed that the stream id of the response to that message will be X.', + name: 'cassandra.response.headers.stream', + type: 'keyword', + }, + 'cassandra.response.headers.op': { + category: 'cassandra', + description: 'An operation type that distinguishes the actual message.', + name: 'cassandra.response.headers.op', + type: 'keyword', + }, + 'cassandra.response.headers.length': { + category: 'cassandra', + description: + 'A integer representing the length of the body of the frame (a frame is limited to 256MB in length).', + name: 'cassandra.response.headers.length', + type: 'long', + }, + 'cassandra.response.result.type': { + category: 'cassandra', + description: 'Cassandra result type.', + name: 'cassandra.response.result.type', + type: 'keyword', + }, + 'cassandra.response.result.rows.num_rows': { + category: 'cassandra', + description: 'Representing the number of rows present in this result.', + name: 'cassandra.response.result.rows.num_rows', + type: 'long', + }, + 'cassandra.response.result.rows.meta.keyspace': { + category: 'cassandra', + description: 'Only present after set Global_tables_spec, the keyspace name.', + name: 'cassandra.response.result.rows.meta.keyspace', + type: 'keyword', + }, + 'cassandra.response.result.rows.meta.table': { + category: 'cassandra', + description: 'Only present after set Global_tables_spec, the table name.', + name: 'cassandra.response.result.rows.meta.table', + type: 'keyword', + }, + 'cassandra.response.result.rows.meta.flags': { + category: 'cassandra', + description: 'Provides information on the formatting of the remaining information.', + name: 'cassandra.response.result.rows.meta.flags', + type: 'keyword', + }, + 'cassandra.response.result.rows.meta.col_count': { + category: 'cassandra', + description: + 'Representing the number of columns selected by the query that produced this result.', + name: 'cassandra.response.result.rows.meta.col_count', + type: 'long', + }, + 'cassandra.response.result.rows.meta.pkey_columns': { + category: 'cassandra', + description: 'Representing the PK columns index and counts.', + name: 'cassandra.response.result.rows.meta.pkey_columns', + type: 'long', + }, + 'cassandra.response.result.rows.meta.paging_state': { + category: 'cassandra', + description: + 'The paging_state is a bytes value that should be used in QUERY/EXECUTE to continue paging and retrieve the remainder of the result for this query.', + name: 'cassandra.response.result.rows.meta.paging_state', + type: 'keyword', + }, + 'cassandra.response.result.keyspace': { + category: 'cassandra', + description: 'Indicating the name of the keyspace that has been set.', + name: 'cassandra.response.result.keyspace', + type: 'keyword', + }, + 'cassandra.response.result.schema_change.change': { + category: 'cassandra', + description: 'Representing the type of changed involved.', + name: 'cassandra.response.result.schema_change.change', + type: 'keyword', + }, + 'cassandra.response.result.schema_change.keyspace': { + category: 'cassandra', + description: 'This describes which keyspace has changed.', + name: 'cassandra.response.result.schema_change.keyspace', + type: 'keyword', + }, + 'cassandra.response.result.schema_change.table': { + category: 'cassandra', + description: 'This describes which table has changed.', + name: 'cassandra.response.result.schema_change.table', + type: 'keyword', + }, + 'cassandra.response.result.schema_change.object': { + category: 'cassandra', + description: + 'This describes the name of said affected object (either the table, user type, function, or aggregate name).', + name: 'cassandra.response.result.schema_change.object', + type: 'keyword', + }, + 'cassandra.response.result.schema_change.target': { + category: 'cassandra', + description: 'Target could be "FUNCTION" or "AGGREGATE", multiple arguments.', + name: 'cassandra.response.result.schema_change.target', + type: 'keyword', + }, + 'cassandra.response.result.schema_change.name': { + category: 'cassandra', + description: 'The function/aggregate name.', + name: 'cassandra.response.result.schema_change.name', + type: 'keyword', + }, + 'cassandra.response.result.schema_change.args': { + category: 'cassandra', + description: 'One string for each argument type (as CQL type).', + name: 'cassandra.response.result.schema_change.args', + type: 'keyword', + }, + 'cassandra.response.result.prepared.prepared_id': { + category: 'cassandra', + description: 'Representing the prepared query ID.', + name: 'cassandra.response.result.prepared.prepared_id', + type: 'keyword', + }, + 'cassandra.response.result.prepared.req_meta.keyspace': { + category: 'cassandra', + description: 'Only present after set Global_tables_spec, the keyspace name.', + name: 'cassandra.response.result.prepared.req_meta.keyspace', + type: 'keyword', + }, + 'cassandra.response.result.prepared.req_meta.table': { + category: 'cassandra', + description: 'Only present after set Global_tables_spec, the table name.', + name: 'cassandra.response.result.prepared.req_meta.table', + type: 'keyword', + }, + 'cassandra.response.result.prepared.req_meta.flags': { + category: 'cassandra', + description: 'Provides information on the formatting of the remaining information.', + name: 'cassandra.response.result.prepared.req_meta.flags', + type: 'keyword', + }, + 'cassandra.response.result.prepared.req_meta.col_count': { + category: 'cassandra', + description: + 'Representing the number of columns selected by the query that produced this result.', + name: 'cassandra.response.result.prepared.req_meta.col_count', + type: 'long', + }, + 'cassandra.response.result.prepared.req_meta.pkey_columns': { + category: 'cassandra', + description: 'Representing the PK columns index and counts.', + name: 'cassandra.response.result.prepared.req_meta.pkey_columns', + type: 'long', + }, + 'cassandra.response.result.prepared.req_meta.paging_state': { + category: 'cassandra', + description: + 'The paging_state is a bytes value that should be used in QUERY/EXECUTE to continue paging and retrieve the remainder of the result for this query.', + name: 'cassandra.response.result.prepared.req_meta.paging_state', + type: 'keyword', + }, + 'cassandra.response.result.prepared.resp_meta.keyspace': { + category: 'cassandra', + description: 'Only present after set Global_tables_spec, the keyspace name.', + name: 'cassandra.response.result.prepared.resp_meta.keyspace', + type: 'keyword', + }, + 'cassandra.response.result.prepared.resp_meta.table': { + category: 'cassandra', + description: 'Only present after set Global_tables_spec, the table name.', + name: 'cassandra.response.result.prepared.resp_meta.table', + type: 'keyword', + }, + 'cassandra.response.result.prepared.resp_meta.flags': { + category: 'cassandra', + description: 'Provides information on the formatting of the remaining information.', + name: 'cassandra.response.result.prepared.resp_meta.flags', + type: 'keyword', + }, + 'cassandra.response.result.prepared.resp_meta.col_count': { + category: 'cassandra', + description: + 'Representing the number of columns selected by the query that produced this result.', + name: 'cassandra.response.result.prepared.resp_meta.col_count', + type: 'long', + }, + 'cassandra.response.result.prepared.resp_meta.pkey_columns': { + category: 'cassandra', + description: 'Representing the PK columns index and counts.', + name: 'cassandra.response.result.prepared.resp_meta.pkey_columns', + type: 'long', + }, + 'cassandra.response.result.prepared.resp_meta.paging_state': { + category: 'cassandra', + description: + 'The paging_state is a bytes value that should be used in QUERY/EXECUTE to continue paging and retrieve the remainder of the result for this query.', + name: 'cassandra.response.result.prepared.resp_meta.paging_state', + type: 'keyword', + }, + 'cassandra.response.supported': { + category: 'cassandra', + description: + 'Indicates which startup options are supported by the server. This message comes as a response to an OPTIONS message.', + name: 'cassandra.response.supported', + type: 'object', + }, + 'cassandra.response.authentication.class': { + category: 'cassandra', + description: 'Indicates the full class name of the IAuthenticator in use', + name: 'cassandra.response.authentication.class', + type: 'keyword', + }, + 'cassandra.response.warnings': { + category: 'cassandra', + description: 'The text of the warnings, only occur when Warning flag was set.', + name: 'cassandra.response.warnings', + type: 'keyword', + }, + 'cassandra.response.event.type': { + category: 'cassandra', + description: 'Representing the event type.', + name: 'cassandra.response.event.type', + type: 'keyword', + }, + 'cassandra.response.event.change': { + category: 'cassandra', + description: + 'The message corresponding respectively to the type of change followed by the address of the new/removed node.', + name: 'cassandra.response.event.change', + type: 'keyword', + }, + 'cassandra.response.event.host': { + category: 'cassandra', + description: 'Representing the node ip.', + name: 'cassandra.response.event.host', + type: 'keyword', + }, + 'cassandra.response.event.port': { + category: 'cassandra', + description: 'Representing the node port.', + name: 'cassandra.response.event.port', + type: 'long', + }, + 'cassandra.response.event.schema_change.change': { + category: 'cassandra', + description: 'Representing the type of changed involved.', + name: 'cassandra.response.event.schema_change.change', + type: 'keyword', + }, + 'cassandra.response.event.schema_change.keyspace': { + category: 'cassandra', + description: 'This describes which keyspace has changed.', + name: 'cassandra.response.event.schema_change.keyspace', + type: 'keyword', + }, + 'cassandra.response.event.schema_change.table': { + category: 'cassandra', + description: 'This describes which table has changed.', + name: 'cassandra.response.event.schema_change.table', + type: 'keyword', + }, + 'cassandra.response.event.schema_change.object': { + category: 'cassandra', + description: + 'This describes the name of said affected object (either the table, user type, function, or aggregate name).', + name: 'cassandra.response.event.schema_change.object', + type: 'keyword', + }, + 'cassandra.response.event.schema_change.target': { + category: 'cassandra', + description: 'Target could be "FUNCTION" or "AGGREGATE", multiple arguments.', + name: 'cassandra.response.event.schema_change.target', + type: 'keyword', + }, + 'cassandra.response.event.schema_change.name': { + category: 'cassandra', + description: 'The function/aggregate name.', + name: 'cassandra.response.event.schema_change.name', + type: 'keyword', + }, + 'cassandra.response.event.schema_change.args': { + category: 'cassandra', + description: 'One string for each argument type (as CQL type).', + name: 'cassandra.response.event.schema_change.args', + type: 'keyword', + }, + 'cassandra.response.error.code': { + category: 'cassandra', + description: 'The error code of the Cassandra response.', + name: 'cassandra.response.error.code', + type: 'long', + }, + 'cassandra.response.error.msg': { + category: 'cassandra', + description: 'The error message of the Cassandra response.', + name: 'cassandra.response.error.msg', + type: 'keyword', + }, + 'cassandra.response.error.type': { + category: 'cassandra', + description: 'The error type of the Cassandra response.', + name: 'cassandra.response.error.type', + type: 'keyword', + }, + 'cassandra.response.error.details.read_consistency': { + category: 'cassandra', + description: 'Representing the consistency level of the query that triggered the exception.', + name: 'cassandra.response.error.details.read_consistency', + type: 'keyword', + }, + 'cassandra.response.error.details.required': { + category: 'cassandra', + description: + 'Representing the number of nodes that should be alive to respect consistency level.', + name: 'cassandra.response.error.details.required', + type: 'long', + }, + 'cassandra.response.error.details.alive': { + category: 'cassandra', + description: + 'Representing the number of replicas that were known to be alive when the request had been processed (since an unavailable exception has been triggered).', + name: 'cassandra.response.error.details.alive', + type: 'long', + }, + 'cassandra.response.error.details.received': { + category: 'cassandra', + description: 'Representing the number of nodes having acknowledged the request.', + name: 'cassandra.response.error.details.received', + type: 'long', + }, + 'cassandra.response.error.details.blockfor': { + category: 'cassandra', + description: + 'Representing the number of replicas whose acknowledgement is required to achieve consistency level.', + name: 'cassandra.response.error.details.blockfor', + type: 'long', + }, + 'cassandra.response.error.details.write_type': { + category: 'cassandra', + description: 'Describe the type of the write that timed out.', + name: 'cassandra.response.error.details.write_type', + type: 'keyword', + }, + 'cassandra.response.error.details.data_present': { + category: 'cassandra', + description: 'It means the replica that was asked for data had responded.', + name: 'cassandra.response.error.details.data_present', + type: 'boolean', + }, + 'cassandra.response.error.details.keyspace': { + category: 'cassandra', + description: 'The keyspace of the failed function.', + name: 'cassandra.response.error.details.keyspace', + type: 'keyword', + }, + 'cassandra.response.error.details.table': { + category: 'cassandra', + description: 'The keyspace of the failed function.', + name: 'cassandra.response.error.details.table', + type: 'keyword', + }, + 'cassandra.response.error.details.stmt_id': { + category: 'cassandra', + description: 'Representing the unknown ID.', + name: 'cassandra.response.error.details.stmt_id', + type: 'keyword', + }, + 'cassandra.response.error.details.num_failures': { + category: 'cassandra', + description: + 'Representing the number of nodes that experience a failure while executing the request.', + name: 'cassandra.response.error.details.num_failures', + type: 'keyword', + }, + 'cassandra.response.error.details.function': { + category: 'cassandra', + description: 'The name of the failed function.', + name: 'cassandra.response.error.details.function', + type: 'keyword', + }, + 'cassandra.response.error.details.arg_types': { + category: 'cassandra', + description: 'One string for each argument type (as CQL type) of the failed function.', + name: 'cassandra.response.error.details.arg_types', + type: 'keyword', + }, + 'dhcpv4.transaction_id': { + category: 'dhcpv4', + description: + 'Transaction ID, a random number chosen by the client, used by the client and server to associate messages and responses between a client and a server. ', + name: 'dhcpv4.transaction_id', + type: 'keyword', + }, + 'dhcpv4.seconds': { + category: 'dhcpv4', + description: + 'Number of seconds elapsed since client began address acquisition or renewal process. ', + name: 'dhcpv4.seconds', + type: 'long', + }, + 'dhcpv4.flags': { + category: 'dhcpv4', + description: + 'Flags are set by the client to indicate how the DHCP server should its reply -- either unicast or broadcast. ', + name: 'dhcpv4.flags', + type: 'keyword', + }, + 'dhcpv4.client_ip': { + category: 'dhcpv4', + description: 'The current IP address of the client.', + name: 'dhcpv4.client_ip', + type: 'ip', + }, + 'dhcpv4.assigned_ip': { + category: 'dhcpv4', + description: + 'The IP address that the DHCP server is assigning to the client. This field is also known as "your" IP address. ', + name: 'dhcpv4.assigned_ip', + type: 'ip', + }, + 'dhcpv4.server_ip': { + category: 'dhcpv4', + description: + 'The IP address of the DHCP server that the client should use for the next step in the bootstrap process. ', + name: 'dhcpv4.server_ip', + type: 'ip', + }, + 'dhcpv4.relay_ip': { + category: 'dhcpv4', + description: + 'The relay IP address used by the client to contact the server (i.e. a DHCP relay server). ', + name: 'dhcpv4.relay_ip', + type: 'ip', + }, + 'dhcpv4.client_mac': { + category: 'dhcpv4', + description: "The client's MAC address (layer two).", + name: 'dhcpv4.client_mac', + type: 'keyword', + }, + 'dhcpv4.server_name': { + category: 'dhcpv4', + description: + 'The name of the server sending the message. Optional. Used in DHCPOFFER or DHCPACK messages. ', + name: 'dhcpv4.server_name', + type: 'keyword', + }, + 'dhcpv4.op_code': { + category: 'dhcpv4', + description: 'The message op code (bootrequest or bootreply). ', + example: 'bootreply', + name: 'dhcpv4.op_code', + type: 'keyword', + }, + 'dhcpv4.hops': { + category: 'dhcpv4', + description: 'The number of hops the DHCP message went through.', + name: 'dhcpv4.hops', + type: 'long', + }, + 'dhcpv4.hardware_type': { + category: 'dhcpv4', + description: 'The type of hardware used for the local network (Ethernet, LocalTalk, etc). ', + name: 'dhcpv4.hardware_type', + type: 'keyword', + }, + 'dhcpv4.option.message_type': { + category: 'dhcpv4', + description: + 'The specific type of DHCP message being sent (e.g. discover, offer, request, decline, ack, nak, release, inform). ', + example: 'ack', + name: 'dhcpv4.option.message_type', + type: 'keyword', + }, + 'dhcpv4.option.parameter_request_list': { + category: 'dhcpv4', + description: + 'This option is used by a DHCP client to request values for specified configuration parameters. ', + name: 'dhcpv4.option.parameter_request_list', + type: 'keyword', + }, + 'dhcpv4.option.requested_ip_address': { + category: 'dhcpv4', + description: + 'This option is used in a client request (DHCPDISCOVER) to allow the client to request that a particular IP address be assigned. ', + name: 'dhcpv4.option.requested_ip_address', + type: 'ip', + }, + 'dhcpv4.option.server_identifier': { + category: 'dhcpv4', + description: 'IP address of the individual DHCP server which handled this message. ', + name: 'dhcpv4.option.server_identifier', + type: 'ip', + }, + 'dhcpv4.option.broadcast_address': { + category: 'dhcpv4', + description: "This option specifies the broadcast address in use on the client's subnet. ", + name: 'dhcpv4.option.broadcast_address', + type: 'ip', + }, + 'dhcpv4.option.max_dhcp_message_size': { + category: 'dhcpv4', + description: + 'This option specifies the maximum length DHCP message that the client is willing to accept. ', + name: 'dhcpv4.option.max_dhcp_message_size', + type: 'long', + }, + 'dhcpv4.option.class_identifier': { + category: 'dhcpv4', + description: + "This option is used by DHCP clients to optionally identify the vendor type and configuration of a DHCP client. Vendors may choose to define specific vendor class identifiers to convey particular configuration or other identification information about a client. For example, the identifier may encode the client's hardware configuration. ", + name: 'dhcpv4.option.class_identifier', + type: 'keyword', + }, + 'dhcpv4.option.domain_name': { + category: 'dhcpv4', + description: + 'This option specifies the domain name that client should use when resolving hostnames via the Domain Name System. ', + name: 'dhcpv4.option.domain_name', + type: 'keyword', + }, + 'dhcpv4.option.dns_servers': { + category: 'dhcpv4', + description: + 'The domain name server option specifies a list of Domain Name System servers available to the client. ', + name: 'dhcpv4.option.dns_servers', + type: 'ip', + }, + 'dhcpv4.option.vendor_identifying_options': { + category: 'dhcpv4', + description: + 'A DHCP client may use this option to unambiguously identify the vendor that manufactured the hardware on which the client is running, the software in use, or an industry consortium to which the vendor belongs. This field is described in RFC 3925. ', + name: 'dhcpv4.option.vendor_identifying_options', + type: 'object', + }, + 'dhcpv4.option.subnet_mask': { + category: 'dhcpv4', + description: 'The subnet mask that the client should use on the currnet network. ', + name: 'dhcpv4.option.subnet_mask', + type: 'ip', + }, + 'dhcpv4.option.utc_time_offset_sec': { + category: 'dhcpv4', + description: + "The time offset field specifies the offset of the client's subnet in seconds from Coordinated Universal Time (UTC). ", + name: 'dhcpv4.option.utc_time_offset_sec', + type: 'long', + }, + 'dhcpv4.option.router': { + category: 'dhcpv4', + description: + "The router option specifies a list of IP addresses for routers on the client's subnet. ", + name: 'dhcpv4.option.router', + type: 'ip', + }, + 'dhcpv4.option.time_servers': { + category: 'dhcpv4', + description: + 'The time server option specifies a list of RFC 868 time servers available to the client. ', + name: 'dhcpv4.option.time_servers', + type: 'ip', + }, + 'dhcpv4.option.ntp_servers': { + category: 'dhcpv4', + description: + 'This option specifies a list of IP addresses indicating NTP servers available to the client. ', + name: 'dhcpv4.option.ntp_servers', + type: 'ip', + }, + 'dhcpv4.option.hostname': { + category: 'dhcpv4', + description: 'This option specifies the name of the client. ', + name: 'dhcpv4.option.hostname', + type: 'keyword', + }, + 'dhcpv4.option.ip_address_lease_time_sec': { + category: 'dhcpv4', + description: + 'This option is used in a client request (DHCPDISCOVER or DHCPREQUEST) to allow the client to request a lease time for the IP address. In a server reply (DHCPOFFER), a DHCP server uses this option to specify the lease time it is willing to offer. ', + name: 'dhcpv4.option.ip_address_lease_time_sec', + type: 'long', + }, + 'dhcpv4.option.message': { + category: 'dhcpv4', + description: + 'This option is used by a DHCP server to provide an error message to a DHCP client in a DHCPNAK message in the event of a failure. A client may use this option in a DHCPDECLINE message to indicate the why the client declined the offered parameters. ', + name: 'dhcpv4.option.message', + type: 'text', + }, + 'dhcpv4.option.renewal_time_sec': { + category: 'dhcpv4', + description: + 'This option specifies the time interval from address assignment until the client transitions to the RENEWING state. ', + name: 'dhcpv4.option.renewal_time_sec', + type: 'long', + }, + 'dhcpv4.option.rebinding_time_sec': { + category: 'dhcpv4', + description: + 'This option specifies the time interval from address assignment until the client transitions to the REBINDING state. ', + name: 'dhcpv4.option.rebinding_time_sec', + type: 'long', + }, + 'dhcpv4.option.boot_file_name': { + category: 'dhcpv4', + description: + "This option is used to identify a bootfile when the 'file' field in the DHCP header has been used for DHCP options. ", + name: 'dhcpv4.option.boot_file_name', + type: 'keyword', + }, + 'dns.flags.authoritative': { + category: 'dns', + description: + 'A DNS flag specifying that the responding server is an authority for the domain name used in the question. ', + name: 'dns.flags.authoritative', + type: 'boolean', + }, + 'dns.flags.recursion_available': { + category: 'dns', + description: + 'A DNS flag specifying whether recursive query support is available in the name server. ', + name: 'dns.flags.recursion_available', + type: 'boolean', + }, + 'dns.flags.recursion_desired': { + category: 'dns', + description: + 'A DNS flag specifying that the client directs the server to pursue a query recursively. Recursive query support is optional. ', + name: 'dns.flags.recursion_desired', + type: 'boolean', + }, + 'dns.flags.authentic_data': { + category: 'dns', + description: + 'A DNS flag specifying that the recursive server considers the response authentic. ', + name: 'dns.flags.authentic_data', + type: 'boolean', + }, + 'dns.flags.checking_disabled': { + category: 'dns', + description: + 'A DNS flag specifying that the client disables the server signature validation of the query. ', + name: 'dns.flags.checking_disabled', + type: 'boolean', + }, + 'dns.flags.truncated_response': { + category: 'dns', + description: 'A DNS flag specifying that only the first 512 bytes of the reply were returned. ', + name: 'dns.flags.truncated_response', + type: 'boolean', + }, + 'dns.question.etld_plus_one': { + category: 'dns', + description: + 'The effective top-level domain (eTLD) plus one more label. For example, the eTLD+1 for "foo.bar.golang.org." is "golang.org.". The data for determining the eTLD comes from an embedded copy of the data from http://publicsuffix.org.', + example: 'amazon.co.uk.', + name: 'dns.question.etld_plus_one', + }, + 'dns.answers_count': { + category: 'dns', + description: 'The number of resource records contained in the `dns.answers` field. ', + name: 'dns.answers_count', + type: 'long', + }, + 'dns.authorities': { + category: 'dns', + description: 'An array containing a dictionary for each authority section from the answer. ', + name: 'dns.authorities', + type: 'object', + }, + 'dns.authorities_count': { + category: 'dns', + description: + 'The number of resource records contained in the `dns.authorities` field. The `dns.authorities` field may or may not be included depending on the configuration of Packetbeat. ', + name: 'dns.authorities_count', + type: 'long', + }, + 'dns.authorities.name': { + category: 'dns', + description: 'The domain name to which this resource record pertains.', + example: 'example.com.', + name: 'dns.authorities.name', + }, + 'dns.authorities.type': { + category: 'dns', + description: 'The type of data contained in this resource record.', + example: 'NS', + name: 'dns.authorities.type', + }, + 'dns.authorities.class': { + category: 'dns', + description: 'The class of DNS data contained in this resource record.', + example: 'IN', + name: 'dns.authorities.class', + }, + 'dns.additionals': { + category: 'dns', + description: 'An array containing a dictionary for each additional section from the answer. ', + name: 'dns.additionals', + type: 'object', + }, + 'dns.additionals_count': { + category: 'dns', + description: + 'The number of resource records contained in the `dns.additionals` field. The `dns.additionals` field may or may not be included depending on the configuration of Packetbeat. ', + name: 'dns.additionals_count', + type: 'long', + }, + 'dns.additionals.name': { + category: 'dns', + description: 'The domain name to which this resource record pertains.', + example: 'example.com.', + name: 'dns.additionals.name', + }, + 'dns.additionals.type': { + category: 'dns', + description: 'The type of data contained in this resource record.', + example: 'NS', + name: 'dns.additionals.type', + }, + 'dns.additionals.class': { + category: 'dns', + description: 'The class of DNS data contained in this resource record.', + example: 'IN', + name: 'dns.additionals.class', + }, + 'dns.additionals.ttl': { + category: 'dns', + description: + 'The time interval in seconds that this resource record may be cached before it should be discarded. Zero values mean that the data should not be cached. ', + name: 'dns.additionals.ttl', + type: 'long', + }, + 'dns.additionals.data': { + category: 'dns', + description: + 'The data describing the resource. The meaning of this data depends on the type and class of the resource record. ', + name: 'dns.additionals.data', + }, + 'dns.opt.version': { + category: 'dns', + description: 'The EDNS version.', + example: '0', + name: 'dns.opt.version', + }, + 'dns.opt.do': { + category: 'dns', + description: 'If set, the transaction uses DNSSEC.', + name: 'dns.opt.do', + type: 'boolean', + }, + 'dns.opt.ext_rcode': { + category: 'dns', + description: 'Extended response code field.', + example: 'BADVERS', + name: 'dns.opt.ext_rcode', + }, + 'dns.opt.udp_size': { + category: 'dns', + description: "Requestor's UDP payload size (in bytes).", + name: 'dns.opt.udp_size', + type: 'long', + }, + 'http.request.headers': { + category: 'http', + description: + 'A map containing the captured header fields from the request. Which headers to capture is configurable. If headers with the same header name are present in the message, they will be separated by commas. ', + name: 'http.request.headers', + type: 'object', + }, + 'http.request.params': { + category: 'http', + name: 'http.request.params', + type: 'alias', + }, + 'http.response.status_phrase': { + category: 'http', + description: 'The HTTP status phrase.', + example: 'Not Found', + name: 'http.response.status_phrase', + }, + 'http.response.headers': { + category: 'http', + description: + 'A map containing the captured header fields from the response. Which headers to capture is configurable. If headers with the same header name are present in the message, they will be separated by commas. ', + name: 'http.response.headers', + type: 'object', + }, + 'http.response.code': { + category: 'http', + name: 'http.response.code', + type: 'alias', + }, + 'http.response.phrase': { + category: 'http', + name: 'http.response.phrase', + type: 'alias', + }, + 'icmp.version': { + category: 'icmp', + description: 'The version of the ICMP protocol.', + name: 'icmp.version', + }, + 'icmp.request.message': { + category: 'icmp', + description: 'A human readable form of the request.', + name: 'icmp.request.message', + type: 'keyword', + }, + 'icmp.request.type': { + category: 'icmp', + description: 'The request type.', + name: 'icmp.request.type', + type: 'long', + }, + 'icmp.request.code': { + category: 'icmp', + description: 'The request code.', + name: 'icmp.request.code', + type: 'long', + }, + 'icmp.response.message': { + category: 'icmp', + description: 'A human readable form of the response.', + name: 'icmp.response.message', + type: 'keyword', + }, + 'icmp.response.type': { + category: 'icmp', + description: 'The response type.', + name: 'icmp.response.type', + type: 'long', + }, + 'icmp.response.code': { + category: 'icmp', + description: 'The response code.', + name: 'icmp.response.code', + type: 'long', + }, + 'memcache.protocol_type': { + category: 'memcache', + description: + 'The memcache protocol implementation. The value can be "binary" for binary-based, "text" for text-based, or "unknown" for an unknown memcache protocol type. ', + name: 'memcache.protocol_type', + type: 'keyword', + }, + 'memcache.request.line': { + category: 'memcache', + description: 'The raw command line for unknown commands ONLY. ', + name: 'memcache.request.line', + type: 'keyword', + }, + 'memcache.request.command': { + category: 'memcache', + description: + 'The memcache command being requested in the memcache text protocol. For example "set" or "get". The binary protocol opcodes are translated into memcache text protocol commands. ', + name: 'memcache.request.command', + type: 'keyword', + }, + 'memcache.response.command': { + category: 'memcache', + description: + 'Either the text based protocol response message type or the name of the originating request if binary protocol is used. ', + name: 'memcache.response.command', + type: 'keyword', + }, + 'memcache.request.type': { + category: 'memcache', + description: + 'The memcache command classification. This value can be "UNKNOWN", "Load", "Store", "Delete", "Counter", "Info", "SlabCtrl", "LRUCrawler", "Stats", "Success", "Fail", or "Auth". ', + name: 'memcache.request.type', + type: 'keyword', + }, + 'memcache.response.type': { + category: 'memcache', + description: + 'The memcache command classification. This value can be "UNKNOWN", "Load", "Store", "Delete", "Counter", "Info", "SlabCtrl", "LRUCrawler", "Stats", "Success", "Fail", or "Auth". The text based protocol will employ any of these, whereas the binary based protocol will mirror the request commands only (see `memcache.response.status` for binary protocol). ', + name: 'memcache.response.type', + type: 'keyword', + }, + 'memcache.response.error_msg': { + category: 'memcache', + description: 'The optional error message in the memcache response (text based protocol only). ', + name: 'memcache.response.error_msg', + type: 'keyword', + }, + 'memcache.request.opcode': { + category: 'memcache', + description: 'The binary protocol message opcode name. ', + name: 'memcache.request.opcode', + type: 'keyword', + }, + 'memcache.response.opcode': { + category: 'memcache', + description: 'The binary protocol message opcode name. ', + name: 'memcache.response.opcode', + type: 'keyword', + }, + 'memcache.request.opcode_value': { + category: 'memcache', + description: 'The binary protocol message opcode value. ', + name: 'memcache.request.opcode_value', + type: 'long', + }, + 'memcache.response.opcode_value': { + category: 'memcache', + description: 'The binary protocol message opcode value. ', + name: 'memcache.response.opcode_value', + type: 'long', + }, + 'memcache.request.opaque': { + category: 'memcache', + description: + 'The binary protocol opaque header value used for correlating request with response messages. ', + name: 'memcache.request.opaque', + type: 'long', + }, + 'memcache.response.opaque': { + category: 'memcache', + description: + 'The binary protocol opaque header value used for correlating request with response messages. ', + name: 'memcache.response.opaque', + type: 'long', + }, + 'memcache.request.vbucket': { + category: 'memcache', + description: 'The vbucket index sent in the binary message. ', + name: 'memcache.request.vbucket', + type: 'long', + }, + 'memcache.response.status': { + category: 'memcache', + description: 'The textual representation of the response error code (binary protocol only). ', + name: 'memcache.response.status', + type: 'keyword', + }, + 'memcache.response.status_code': { + category: 'memcache', + description: 'The status code value returned in the response (binary protocol only). ', + name: 'memcache.response.status_code', + type: 'long', + }, + 'memcache.request.keys': { + category: 'memcache', + description: 'The list of keys sent in the store or load commands. ', + name: 'memcache.request.keys', + type: 'array', + }, + 'memcache.response.keys': { + category: 'memcache', + description: 'The list of keys returned for the load command (if present). ', + name: 'memcache.response.keys', + type: 'array', + }, + 'memcache.request.count_values': { + category: 'memcache', + description: + 'The number of values found in the memcache request message. If the command does not send any data, this field is missing. ', + name: 'memcache.request.count_values', + type: 'long', + }, + 'memcache.response.count_values': { + category: 'memcache', + description: + 'The number of values found in the memcache response message. If the command does not send any data, this field is missing. ', + name: 'memcache.response.count_values', + type: 'long', + }, + 'memcache.request.values': { + category: 'memcache', + description: 'The list of base64 encoded values sent with the request (if present). ', + name: 'memcache.request.values', + type: 'array', + }, + 'memcache.response.values': { + category: 'memcache', + description: 'The list of base64 encoded values sent with the response (if present). ', + name: 'memcache.response.values', + type: 'array', + }, + 'memcache.request.bytes': { + category: 'memcache', + description: 'The byte count of the values being transferred. ', + name: 'memcache.request.bytes', + type: 'long', + format: 'bytes', + }, + 'memcache.response.bytes': { + category: 'memcache', + description: 'The byte count of the values being transferred. ', + name: 'memcache.response.bytes', + type: 'long', + format: 'bytes', + }, + 'memcache.request.delta': { + category: 'memcache', + description: 'The counter increment/decrement delta value. ', + name: 'memcache.request.delta', + type: 'long', + }, + 'memcache.request.initial': { + category: 'memcache', + description: 'The counter increment/decrement initial value parameter (binary protocol only). ', + name: 'memcache.request.initial', + type: 'long', + }, + 'memcache.request.verbosity': { + category: 'memcache', + description: 'The value of the memcache "verbosity" command. ', + name: 'memcache.request.verbosity', + type: 'long', + }, + 'memcache.request.raw_args': { + category: 'memcache', + description: + 'The text protocol raw arguments for the "stats ..." and "lru crawl ..." commands. ', + name: 'memcache.request.raw_args', + type: 'keyword', + }, + 'memcache.request.source_class': { + category: 'memcache', + description: "The source class id in 'slab reassign' command. ", + name: 'memcache.request.source_class', + type: 'long', + }, + 'memcache.request.dest_class': { + category: 'memcache', + description: "The destination class id in 'slab reassign' command. ", + name: 'memcache.request.dest_class', + type: 'long', + }, + 'memcache.request.automove': { + category: 'memcache', + description: + 'The automove mode in the \'slab automove\' command expressed as a string. This value can be "standby"(=0), "slow"(=1), "aggressive"(=2), or the raw value if the value is unknown. ', + name: 'memcache.request.automove', + type: 'keyword', + }, + 'memcache.request.flags': { + category: 'memcache', + description: 'The memcache command flags sent in the request (if present). ', + name: 'memcache.request.flags', + type: 'long', + }, + 'memcache.response.flags': { + category: 'memcache', + description: 'The memcache message flags sent in the response (if present). ', + name: 'memcache.response.flags', + type: 'long', + }, + 'memcache.request.exptime': { + category: 'memcache', + description: + 'The data expiry time in seconds sent with the memcache command (if present). If the value is <30 days, the expiry time is relative to "now", or else it is an absolute Unix time in seconds (32-bit). ', + name: 'memcache.request.exptime', + type: 'long', + }, + 'memcache.request.sleep_us': { + category: 'memcache', + description: "The sleep setting in microseconds for the 'lru_crawler sleep' command. ", + name: 'memcache.request.sleep_us', + type: 'long', + }, + 'memcache.response.value': { + category: 'memcache', + description: 'The counter value returned by a counter operation. ', + name: 'memcache.response.value', + type: 'long', + }, + 'memcache.request.noreply': { + category: 'memcache', + description: + 'Set to true if noreply was set in the request. The `memcache.response` field will be missing. ', + name: 'memcache.request.noreply', + type: 'boolean', + }, + 'memcache.request.quiet': { + category: 'memcache', + description: 'Set to true if the binary protocol message is to be treated as a quiet message. ', + name: 'memcache.request.quiet', + type: 'boolean', + }, + 'memcache.request.cas_unique': { + category: 'memcache', + description: 'The CAS (compare-and-swap) identifier if present. ', + name: 'memcache.request.cas_unique', + type: 'long', + }, + 'memcache.response.cas_unique': { + category: 'memcache', + description: + 'The CAS (compare-and-swap) identifier to be used with CAS-based updates (if present). ', + name: 'memcache.response.cas_unique', + type: 'long', + }, + 'memcache.response.stats': { + category: 'memcache', + description: + 'The list of statistic values returned. Each entry is a dictionary with the fields "name" and "value". ', + name: 'memcache.response.stats', + type: 'array', + }, + 'memcache.response.version': { + category: 'memcache', + description: 'The returned memcache version string. ', + name: 'memcache.response.version', + type: 'keyword', + }, + 'mongodb.error': { + category: 'mongodb', + description: + 'If the MongoDB request has resulted in an error, this field contains the error message returned by the server. ', + name: 'mongodb.error', + }, + 'mongodb.fullCollectionName': { + category: 'mongodb', + description: + 'The full collection name. The full collection name is the concatenation of the database name with the collection name, using a dot (.) for the concatenation. For example, for the database foo and the collection bar, the full collection name is foo.bar. ', + name: 'mongodb.fullCollectionName', + }, + 'mongodb.numberToSkip': { + category: 'mongodb', + description: + 'Sets the number of documents to omit - starting from the first document in the resulting dataset - when returning the result of the query. ', + name: 'mongodb.numberToSkip', + type: 'long', + }, + 'mongodb.numberToReturn': { + category: 'mongodb', + description: 'The requested maximum number of documents to be returned. ', + name: 'mongodb.numberToReturn', + type: 'long', + }, + 'mongodb.numberReturned': { + category: 'mongodb', + description: 'The number of documents in the reply. ', + name: 'mongodb.numberReturned', + type: 'long', + }, + 'mongodb.startingFrom': { + category: 'mongodb', + description: 'Where in the cursor this reply is starting. ', + name: 'mongodb.startingFrom', + }, + 'mongodb.query': { + category: 'mongodb', + description: + 'A JSON document that represents the query. The query will contain one or more elements, all of which must match for a document to be included in the result set. Possible elements include $query, $orderby, $hint, $explain, and $snapshot. ', + name: 'mongodb.query', + }, + 'mongodb.returnFieldsSelector': { + category: 'mongodb', + description: + 'A JSON document that limits the fields in the returned documents. The returnFieldsSelector contains one or more elements, each of which is the name of a field that should be returned, and the integer value 1. ', + name: 'mongodb.returnFieldsSelector', + }, + 'mongodb.selector': { + category: 'mongodb', + description: + 'A BSON document that specifies the query for selecting the document to update or delete. ', + name: 'mongodb.selector', + }, + 'mongodb.update': { + category: 'mongodb', + description: + 'A BSON document that specifies the update to be performed. For information on specifying updates, see the Update Operations documentation from the MongoDB Manual. ', + name: 'mongodb.update', + }, + 'mongodb.cursorId': { + category: 'mongodb', + description: + 'The cursor identifier returned in the OP_REPLY. This must be the value that was returned from the database. ', + name: 'mongodb.cursorId', + }, + 'mysql.affected_rows': { + category: 'mysql', + description: + 'If the MySQL command is successful, this field contains the affected number of rows of the last statement. ', + name: 'mysql.affected_rows', + type: 'long', + }, + 'mysql.insert_id': { + category: 'mysql', + description: + 'If the INSERT query is successful, this field contains the id of the newly inserted row. ', + name: 'mysql.insert_id', + }, + 'mysql.num_fields': { + category: 'mysql', + description: + 'If the SELECT query is successful, this field is set to the number of fields returned. ', + name: 'mysql.num_fields', + }, + 'mysql.num_rows': { + category: 'mysql', + description: + 'If the SELECT query is successful, this field is set to the number of rows returned. ', + name: 'mysql.num_rows', + }, + 'mysql.query': { + category: 'mysql', + description: "The row mysql query as read from the transaction's request. ", + name: 'mysql.query', + }, + 'mysql.error_code': { + category: 'mysql', + description: 'The error code returned by MySQL. ', + name: 'mysql.error_code', + type: 'long', + }, + 'mysql.error_message': { + category: 'mysql', + description: 'The error info message returned by MySQL. ', + name: 'mysql.error_message', + }, + 'nfs.version': { + category: 'nfs', + description: 'NFS protocol version number.', + name: 'nfs.version', + type: 'long', + }, + 'nfs.minor_version': { + category: 'nfs', + description: 'NFS protocol minor version number.', + name: 'nfs.minor_version', + type: 'long', + }, + 'nfs.tag': { + category: 'nfs', + description: 'NFS v4 COMPOUND operation tag.', + name: 'nfs.tag', + }, + 'nfs.opcode': { + category: 'nfs', + description: 'NFS operation name, or main operation name, in case of COMPOUND calls. ', + name: 'nfs.opcode', + }, + 'nfs.status': { + category: 'nfs', + description: 'NFS operation reply status.', + name: 'nfs.status', + }, + 'rpc.xid': { + category: 'rpc', + description: 'RPC message transaction identifier.', + name: 'rpc.xid', + }, + 'rpc.status': { + category: 'rpc', + description: 'RPC message reply status.', + name: 'rpc.status', + }, + 'rpc.auth_flavor': { + category: 'rpc', + description: 'RPC authentication flavor.', + name: 'rpc.auth_flavor', + }, + 'rpc.cred.uid': { + category: 'rpc', + description: "RPC caller's user id, in case of auth-unix.", + name: 'rpc.cred.uid', + type: 'long', + }, + 'rpc.cred.gid': { + category: 'rpc', + description: "RPC caller's group id, in case of auth-unix.", + name: 'rpc.cred.gid', + type: 'long', + }, + 'rpc.cred.gids': { + category: 'rpc', + description: "RPC caller's secondary group ids, in case of auth-unix.", + name: 'rpc.cred.gids', + }, + 'rpc.cred.stamp': { + category: 'rpc', + description: 'Arbitrary ID which the caller machine may generate.', + name: 'rpc.cred.stamp', + type: 'long', + }, + 'rpc.cred.machinename': { + category: 'rpc', + description: "The name of the caller's machine.", + name: 'rpc.cred.machinename', + }, + 'rpc.call_size': { + category: 'rpc', + description: 'RPC call size with argument.', + name: 'rpc.call_size', + type: 'alias', + }, + 'rpc.reply_size': { + category: 'rpc', + description: 'RPC reply size with argument.', + name: 'rpc.reply_size', + type: 'alias', + }, + 'pgsql.error_code': { + category: 'pgsql', + description: 'The PostgreSQL error code.', + name: 'pgsql.error_code', + type: 'long', + }, + 'pgsql.error_message': { + category: 'pgsql', + description: 'The PostgreSQL error message.', + name: 'pgsql.error_message', + }, + 'pgsql.error_severity': { + category: 'pgsql', + description: 'The PostgreSQL error severity.', + name: 'pgsql.error_severity', + }, + 'pgsql.num_fields': { + category: 'pgsql', + description: + 'If the SELECT query if successful, this field is set to the number of fields returned. ', + name: 'pgsql.num_fields', + }, + 'pgsql.num_rows': { + category: 'pgsql', + description: + 'If the SELECT query if successful, this field is set to the number of rows returned. ', + name: 'pgsql.num_rows', + }, + 'redis.return_value': { + category: 'redis', + description: 'The return value of the Redis command in a human readable format. ', + name: 'redis.return_value', + }, + 'redis.error': { + category: 'redis', + description: + 'If the Redis command has resulted in an error, this field contains the error message returned by the Redis server. ', + name: 'redis.error', + }, + 'thrift.params': { + category: 'thrift', + description: + 'The RPC method call parameters in a human readable format. If the IDL files are available, the parameters use names whenever possible. Otherwise, the IDs from the message are used. ', + name: 'thrift.params', + }, + 'thrift.service': { + category: 'thrift', + description: 'The name of the Thrift-RPC service as defined in the IDL files. ', + name: 'thrift.service', + }, + 'thrift.return_value': { + category: 'thrift', + description: + 'The value returned by the Thrift-RPC call. This is encoded in a human readable format. ', + name: 'thrift.return_value', + }, + 'thrift.exceptions': { + category: 'thrift', + description: + 'If the call resulted in exceptions, this field contains the exceptions in a human readable format. ', + name: 'thrift.exceptions', + }, + 'tls.client.x509.version': { + category: 'tls', + description: 'Version of x509 format.', + example: 3, + name: 'tls.client.x509.version', + type: 'keyword', + }, + 'tls.client.x509.version_number': { + category: 'tls', + description: 'Version of x509 format.', + example: 3, + name: 'tls.client.x509.version_number', + type: 'keyword', + }, + 'tls.client.x509.serial_number': { + category: 'tls', + description: + 'Unique serial number issued by the certificate authority. For consistency, if this value is alphanumeric, it should be formatted without colons and uppercase characters. ', + example: '55FBB9C7DEBF09809D12CCAA', + name: 'tls.client.x509.serial_number', + type: 'keyword', + }, + 'tls.client.x509.issuer.distinguished_name': { + category: 'tls', + description: 'Distinguished name (DN) of issuing certificate authority.', + example: 'C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance Server CA', + name: 'tls.client.x509.issuer.distinguished_name', + type: 'keyword', + }, + 'tls.client.x509.issuer.common_name': { + category: 'tls', + description: 'List of common name (CN) of issuing certificate authority.', + example: 'DigiCert SHA2 High Assurance Server CA', + name: 'tls.client.x509.issuer.common_name', + type: 'keyword', + }, + 'tls.client.x509.issuer.organizational_unit': { + category: 'tls', + description: 'List of organizational units (OU) of issuing certificate authority.', + example: 'www.digicert.com', + name: 'tls.client.x509.issuer.organizational_unit', + type: 'keyword', + }, + 'tls.client.x509.issuer.organization': { + category: 'tls', + description: 'List of organizations (O) of issuing certificate authority.', + example: 'DigiCert Inc', + name: 'tls.client.x509.issuer.organization', + type: 'keyword', + }, + 'tls.client.x509.issuer.locality': { + category: 'tls', + description: 'List of locality names (L)', + example: 'Mountain View', + name: 'tls.client.x509.issuer.locality', + type: 'keyword', + }, + 'tls.client.x509.issuer.province': { + category: 'tls', + description: 'Province or region within country.', + name: 'tls.client.x509.issuer.province', + type: 'keyword', + }, + 'tls.client.x509.issuer.state_or_province': { + category: 'tls', + description: 'List of state or province names (ST, S, or P)', + example: 'California', + name: 'tls.client.x509.issuer.state_or_province', + type: 'keyword', + }, + 'tls.client.x509.issuer.country': { + category: 'tls', + description: 'List of country (C) codes', + example: 'US', + name: 'tls.client.x509.issuer.country', + type: 'keyword', + }, + 'tls.client.x509.signature_algorithm': { + category: 'tls', + description: + 'Identifier for certificate signature algorithm. Recommend using names found in Go Lang Crypto library (See https://github.com/golang/go/blob/go1.14/src/crypto/x509/x509.go#L337-L353).', + example: 'SHA256-RSA', + name: 'tls.client.x509.signature_algorithm', + type: 'keyword', + }, + 'tls.client.x509.not_before': { + category: 'tls', + description: 'Time at which the certificate is first considered valid.', + example: '"2019-08-16T01:40:25.000Z"', + name: 'tls.client.x509.not_before', + type: 'date', + }, + 'tls.client.x509.not_after': { + category: 'tls', + description: 'Time at which the certificate is no longer considered valid.', + example: '"2020-07-16T03:15:39.000Z"', + name: 'tls.client.x509.not_after', + type: 'date', + }, + 'tls.client.x509.subject.distinguished_name': { + category: 'tls', + description: 'Distinguished name (DN) of the certificate subject entity.', + example: 'C=US, ST=California, L=San Francisco, O=Fastly, Inc., CN=r2.shared.global.fastly.net', + name: 'tls.client.x509.subject.distinguished_name', + type: 'keyword', + }, + 'tls.client.x509.subject.common_name': { + category: 'tls', + description: 'List of common names (CN) of subject.', + example: 'r2.shared.global.fastly.net', + name: 'tls.client.x509.subject.common_name', + type: 'keyword', + }, + 'tls.client.x509.subject.organizational_unit': { + category: 'tls', + description: 'List of organizational units (OU) of subject.', + name: 'tls.client.x509.subject.organizational_unit', + type: 'keyword', + }, + 'tls.client.x509.subject.organization': { + category: 'tls', + description: 'List of organizations (O) of subject.', + example: 'Fastly, Inc.', + name: 'tls.client.x509.subject.organization', + type: 'keyword', + }, + 'tls.client.x509.subject.locality': { + category: 'tls', + description: 'List of locality names (L)', + example: 'San Francisco', + name: 'tls.client.x509.subject.locality', + type: 'keyword', + }, + 'tls.client.x509.subject.province': { + category: 'tls', + description: 'Province or region within country.', + name: 'tls.client.x509.subject.province', + type: 'keyword', + }, + 'tls.client.x509.subject.state_or_province': { + category: 'tls', + description: 'List of state or province names (ST, S, or P)', + example: 'California', + name: 'tls.client.x509.subject.state_or_province', + type: 'keyword', + }, + 'tls.client.x509.subject.country': { + category: 'tls', + description: 'List of country (C) code', + example: 'US', + name: 'tls.client.x509.subject.country', + type: 'keyword', + }, + 'tls.client.x509.public_key_algorithm': { + category: 'tls', + description: 'Algorithm used to generate the public key.', + example: 'RSA', + name: 'tls.client.x509.public_key_algorithm', + type: 'keyword', + }, + 'tls.client.x509.public_key_size': { + category: 'tls', + description: 'The size of the public key space in bits.', + example: 2048, + name: 'tls.client.x509.public_key_size', + type: 'long', + }, + 'tls.client.x509.alternative_names': { + category: 'tls', + description: + 'List of subject alternative names (SAN). Name types vary by certificate authority and certificate type but commonly contain IP addresses, DNS names (and wildcards), and email addresses.', + example: '*.elastic.co', + name: 'tls.client.x509.alternative_names', + type: 'keyword', + }, + 'tls.server.x509.version': { + category: 'tls', + description: 'Version of x509 format.', + example: 3, + name: 'tls.server.x509.version', + type: 'keyword', + }, + 'tls.server.x509.version_number': { + category: 'tls', + description: 'Version of x509 format.', + example: 3, + name: 'tls.server.x509.version_number', + type: 'keyword', + }, + 'tls.server.x509.serial_number': { + category: 'tls', + description: + 'Unique serial number issued by the certificate authority. For consistency, if this value is alphanumeric, it should be formatted without colons and uppercase characters. ', + example: '55FBB9C7DEBF09809D12CCAA', + name: 'tls.server.x509.serial_number', + type: 'keyword', + }, + 'tls.server.x509.issuer.distinguished_name': { + category: 'tls', + description: 'Distinguished name (DN) of issuing certificate authority.', + example: 'C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance Server CA', + name: 'tls.server.x509.issuer.distinguished_name', + type: 'keyword', + }, + 'tls.server.x509.issuer.common_name': { + category: 'tls', + description: 'List of common name (CN) of issuing certificate authority.', + example: 'DigiCert SHA2 High Assurance Server CA', + name: 'tls.server.x509.issuer.common_name', + type: 'keyword', + }, + 'tls.server.x509.issuer.organizational_unit': { + category: 'tls', + description: 'List of organizational units (OU) of issuing certificate authority.', + example: 'www.digicert.com', + name: 'tls.server.x509.issuer.organizational_unit', + type: 'keyword', + }, + 'tls.server.x509.issuer.organization': { + category: 'tls', + description: 'List of organizations (O) of issuing certificate authority.', + example: 'DigiCert Inc', + name: 'tls.server.x509.issuer.organization', + type: 'keyword', + }, + 'tls.server.x509.issuer.locality': { + category: 'tls', + description: 'List of locality names (L)', + example: 'Mountain View', + name: 'tls.server.x509.issuer.locality', + type: 'keyword', + }, + 'tls.server.x509.issuer.province': { + category: 'tls', + description: 'Province or region within country.', + name: 'tls.server.x509.issuer.province', + type: 'keyword', + }, + 'tls.server.x509.issuer.state_or_province': { + category: 'tls', + description: 'List of state or province names (ST, S, or P)', + example: 'California', + name: 'tls.server.x509.issuer.state_or_province', + type: 'keyword', + }, + 'tls.server.x509.issuer.country': { + category: 'tls', + description: 'List of country (C) codes', + example: 'US', + name: 'tls.server.x509.issuer.country', + type: 'keyword', + }, + 'tls.server.x509.signature_algorithm': { + category: 'tls', + description: + 'Identifier for certificate signature algorithm. Recommend using names found in Go Lang Crypto library (See https://github.com/golang/go/blob/go1.14/src/crypto/x509/x509.go#L337-L353).', + example: 'SHA256-RSA', + name: 'tls.server.x509.signature_algorithm', + type: 'keyword', + }, + 'tls.server.x509.not_before': { + category: 'tls', + description: 'Time at which the certificate is first considered valid.', + example: '"2019-08-16T01:40:25.000Z"', + name: 'tls.server.x509.not_before', + type: 'date', + }, + 'tls.server.x509.not_after': { + category: 'tls', + description: 'Time at which the certificate is no longer considered valid.', + example: '"2020-07-16T03:15:39.000Z"', + name: 'tls.server.x509.not_after', + type: 'date', + }, + 'tls.server.x509.subject.distinguished_name': { + category: 'tls', + description: 'Distinguished name (DN) of the certificate subject entity.', + example: 'C=US, ST=California, L=San Francisco, O=Fastly, Inc., CN=r2.shared.global.fastly.net', + name: 'tls.server.x509.subject.distinguished_name', + type: 'keyword', + }, + 'tls.server.x509.subject.common_name': { + category: 'tls', + description: 'List of common names (CN) of subject.', + example: 'r2.shared.global.fastly.net', + name: 'tls.server.x509.subject.common_name', + type: 'keyword', + }, + 'tls.server.x509.subject.organizational_unit': { + category: 'tls', + description: 'List of organizational units (OU) of subject.', + name: 'tls.server.x509.subject.organizational_unit', + type: 'keyword', + }, + 'tls.server.x509.subject.organization': { + category: 'tls', + description: 'List of organizations (O) of subject.', + example: 'Fastly, Inc.', + name: 'tls.server.x509.subject.organization', + type: 'keyword', + }, + 'tls.server.x509.subject.locality': { + category: 'tls', + description: 'List of locality names (L)', + example: 'San Francisco', + name: 'tls.server.x509.subject.locality', + type: 'keyword', + }, + 'tls.server.x509.subject.province': { + category: 'tls', + description: 'Province or region within country.', + name: 'tls.server.x509.subject.province', + type: 'keyword', + }, + 'tls.server.x509.subject.state_or_province': { + category: 'tls', + description: 'List of state or province names (ST, S, or P)', + example: 'California', + name: 'tls.server.x509.subject.state_or_province', + type: 'keyword', + }, + 'tls.server.x509.subject.country': { + category: 'tls', + description: 'List of country (C) code', + example: 'US', + name: 'tls.server.x509.subject.country', + type: 'keyword', + }, + 'tls.server.x509.public_key_algorithm': { + category: 'tls', + description: 'Algorithm used to generate the public key.', + example: 'RSA', + name: 'tls.server.x509.public_key_algorithm', + type: 'keyword', + }, + 'tls.server.x509.public_key_size': { + category: 'tls', + description: 'The size of the public key space in bits.', + example: 2048, + name: 'tls.server.x509.public_key_size', + type: 'long', + }, + 'tls.server.x509.alternative_names': { + category: 'tls', + description: + 'List of subject alternative names (SAN). Name types vary by certificate authority and certificate type but commonly contain IP addresses, DNS names (and wildcards), and email addresses.', + example: '*.elastic.co', + name: 'tls.server.x509.alternative_names', + type: 'keyword', + }, + 'tls.detailed.version': { + category: 'tls', + description: 'The version of the TLS protocol used. ', + example: 'TLS 1.3', + name: 'tls.detailed.version', + type: 'keyword', + }, + 'tls.detailed.resumption_method': { + category: 'tls', + description: + 'If the session has been resumed, the underlying method used. One of "id" for TLS session ID or "ticket" for TLS ticket extension. ', + name: 'tls.detailed.resumption_method', + type: 'keyword', + }, + 'tls.detailed.client_certificate_requested': { + category: 'tls', + description: + 'Whether the server has requested the client to authenticate itself using a client certificate. ', + name: 'tls.detailed.client_certificate_requested', + type: 'boolean', + }, + 'tls.detailed.client_hello.version': { + category: 'tls', + description: + 'The version of the TLS protocol by which the client wishes to communicate during this session. ', + name: 'tls.detailed.client_hello.version', + type: 'keyword', + }, + 'tls.detailed.client_hello.session_id': { + category: 'tls', + description: + 'Unique number to identify the session for the corresponding connection with the client. ', + name: 'tls.detailed.client_hello.session_id', + type: 'keyword', + }, + 'tls.detailed.client_hello.supported_compression_methods': { + category: 'tls', + description: + 'The list of compression methods the client supports. See https://www.iana.org/assignments/comp-meth-ids/comp-meth-ids.xhtml ', + name: 'tls.detailed.client_hello.supported_compression_methods', + type: 'keyword', + }, + 'tls.detailed.client_hello.extensions.server_name_indication': { + category: 'tls', + description: 'List of hostnames', + name: 'tls.detailed.client_hello.extensions.server_name_indication', + type: 'keyword', + }, + 'tls.detailed.client_hello.extensions.application_layer_protocol_negotiation': { + category: 'tls', + description: 'List of application-layer protocols the client is willing to use. ', + name: 'tls.detailed.client_hello.extensions.application_layer_protocol_negotiation', + type: 'keyword', + }, + 'tls.detailed.client_hello.extensions.session_ticket': { + category: 'tls', + description: + 'Length of the session ticket, if provided, or an empty string to advertise support for tickets. ', + name: 'tls.detailed.client_hello.extensions.session_ticket', + type: 'keyword', + }, + 'tls.detailed.client_hello.extensions.supported_versions': { + category: 'tls', + description: 'List of TLS versions that the client is willing to use. ', + name: 'tls.detailed.client_hello.extensions.supported_versions', + type: 'keyword', + }, + 'tls.detailed.client_hello.extensions.supported_groups': { + category: 'tls', + description: 'List of Elliptic Curve Cryptography (ECC) curve groups supported by the client. ', + name: 'tls.detailed.client_hello.extensions.supported_groups', + type: 'keyword', + }, + 'tls.detailed.client_hello.extensions.signature_algorithms': { + category: 'tls', + description: 'List of signature algorithms that may be use in digital signatures. ', + name: 'tls.detailed.client_hello.extensions.signature_algorithms', + type: 'keyword', + }, + 'tls.detailed.client_hello.extensions.ec_points_formats': { + category: 'tls', + description: + 'List of Elliptic Curve (EC) point formats. Indicates the set of point formats that the client can parse. ', + name: 'tls.detailed.client_hello.extensions.ec_points_formats', + type: 'keyword', + }, + 'tls.detailed.client_hello.extensions._unparsed_': { + category: 'tls', + description: 'List of extensions that were left unparsed by Packetbeat. ', + name: 'tls.detailed.client_hello.extensions._unparsed_', + type: 'keyword', + }, + 'tls.detailed.server_hello.version': { + category: 'tls', + description: + 'The version of the TLS protocol that is used for this session. It is the highest version supported by the server not exceeding the version requested in the client hello. ', + name: 'tls.detailed.server_hello.version', + type: 'keyword', + }, + 'tls.detailed.server_hello.selected_compression_method': { + category: 'tls', + description: + 'The compression method selected by the server from the list provided in the client hello. ', + name: 'tls.detailed.server_hello.selected_compression_method', + type: 'keyword', + }, + 'tls.detailed.server_hello.session_id': { + category: 'tls', + description: + 'Unique number to identify the session for the corresponding connection with the client. ', + name: 'tls.detailed.server_hello.session_id', + type: 'keyword', + }, + 'tls.detailed.server_hello.extensions.application_layer_protocol_negotiation': { + category: 'tls', + description: 'Negotiated application layer protocol', + name: 'tls.detailed.server_hello.extensions.application_layer_protocol_negotiation', + type: 'keyword', + }, + 'tls.detailed.server_hello.extensions.session_ticket': { + category: 'tls', + description: + 'Used to announce that a session ticket will be provided by the server. Always an empty string. ', + name: 'tls.detailed.server_hello.extensions.session_ticket', + type: 'keyword', + }, + 'tls.detailed.server_hello.extensions.supported_versions': { + category: 'tls', + description: 'Negotiated TLS version to be used. ', + name: 'tls.detailed.server_hello.extensions.supported_versions', + type: 'keyword', + }, + 'tls.detailed.server_hello.extensions.ec_points_formats': { + category: 'tls', + description: + 'List of Elliptic Curve (EC) point formats. Indicates the set of point formats that the server can parse. ', + name: 'tls.detailed.server_hello.extensions.ec_points_formats', + type: 'keyword', + }, + 'tls.detailed.server_hello.extensions._unparsed_': { + category: 'tls', + description: 'List of extensions that were left unparsed by Packetbeat. ', + name: 'tls.detailed.server_hello.extensions._unparsed_', + type: 'keyword', + }, + 'tls.detailed.client_certificate.version': { + category: 'tls', + description: 'X509 format version.', + name: 'tls.detailed.client_certificate.version', + type: 'long', + }, + 'tls.detailed.client_certificate.version_number': { + category: 'tls', + description: 'Version of x509 format.', + example: 3, + name: 'tls.detailed.client_certificate.version_number', + type: 'keyword', + }, + 'tls.detailed.client_certificate.serial_number': { + category: 'tls', + description: "The certificate's serial number.", + name: 'tls.detailed.client_certificate.serial_number', + type: 'keyword', + }, + 'tls.detailed.client_certificate.not_before': { + category: 'tls', + description: 'Date before which the certificate is not valid.', + name: 'tls.detailed.client_certificate.not_before', + type: 'date', + }, + 'tls.detailed.client_certificate.not_after': { + category: 'tls', + description: 'Date after which the certificate expires.', + name: 'tls.detailed.client_certificate.not_after', + type: 'date', + }, + 'tls.detailed.client_certificate.public_key_algorithm': { + category: 'tls', + description: "The algorithm used for this certificate's public key. One of RSA, DSA or ECDSA. ", + name: 'tls.detailed.client_certificate.public_key_algorithm', + type: 'keyword', + }, + 'tls.detailed.client_certificate.public_key_size': { + category: 'tls', + description: 'Size of the public key.', + name: 'tls.detailed.client_certificate.public_key_size', + type: 'long', + }, + 'tls.detailed.client_certificate.signature_algorithm': { + category: 'tls', + description: "The algorithm used for the certificate's signature. ", + name: 'tls.detailed.client_certificate.signature_algorithm', + type: 'keyword', + }, + 'tls.detailed.client_certificate.alternative_names': { + category: 'tls', + description: 'Subject Alternative Names for this certificate.', + name: 'tls.detailed.client_certificate.alternative_names', + type: 'keyword', + }, + 'tls.detailed.client_certificate.subject.country': { + category: 'tls', + description: 'Country code.', + name: 'tls.detailed.client_certificate.subject.country', + type: 'keyword', + }, + 'tls.detailed.client_certificate.subject.organization': { + category: 'tls', + description: 'Organization name.', + name: 'tls.detailed.client_certificate.subject.organization', + type: 'keyword', + }, + 'tls.detailed.client_certificate.subject.organizational_unit': { + category: 'tls', + description: 'Unit within organization.', + name: 'tls.detailed.client_certificate.subject.organizational_unit', + type: 'keyword', + }, + 'tls.detailed.client_certificate.subject.province': { + category: 'tls', + description: 'Province or region within country.', + name: 'tls.detailed.client_certificate.subject.province', + type: 'keyword', + }, + 'tls.detailed.client_certificate.subject.common_name': { + category: 'tls', + description: 'Name or host name identified by the certificate.', + name: 'tls.detailed.client_certificate.subject.common_name', + type: 'keyword', + }, + 'tls.detailed.client_certificate.subject.locality': { + category: 'tls', + description: 'Locality.', + name: 'tls.detailed.client_certificate.subject.locality', + type: 'keyword', + }, + 'tls.detailed.client_certificate.subject.distinguished_name': { + category: 'tls', + description: 'Distinguished name (DN) of the certificate subject entity.', + example: 'C=US, ST=California, L=San Francisco, O=Fastly, Inc., CN=r2.shared.global.fastly.net', + name: 'tls.detailed.client_certificate.subject.distinguished_name', + type: 'keyword', + }, + 'tls.detailed.client_certificate.issuer.country': { + category: 'tls', + description: 'Country code.', + name: 'tls.detailed.client_certificate.issuer.country', + type: 'keyword', + }, + 'tls.detailed.client_certificate.issuer.organization': { + category: 'tls', + description: 'Organization name.', + name: 'tls.detailed.client_certificate.issuer.organization', + type: 'keyword', + }, + 'tls.detailed.client_certificate.issuer.organizational_unit': { + category: 'tls', + description: 'Unit within organization.', + name: 'tls.detailed.client_certificate.issuer.organizational_unit', + type: 'keyword', + }, + 'tls.detailed.client_certificate.issuer.province': { + category: 'tls', + description: 'Province or region within country.', + name: 'tls.detailed.client_certificate.issuer.province', + type: 'keyword', + }, + 'tls.detailed.client_certificate.issuer.common_name': { + category: 'tls', + description: 'Name or host name identified by the certificate.', + name: 'tls.detailed.client_certificate.issuer.common_name', + type: 'keyword', + }, + 'tls.detailed.client_certificate.issuer.locality': { + category: 'tls', + description: 'Locality.', + name: 'tls.detailed.client_certificate.issuer.locality', + type: 'keyword', + }, + 'tls.detailed.client_certificate.issuer.distinguished_name': { + category: 'tls', + description: 'Distinguished name (DN) of the certificate issuer entity.', + example: 'C=US, ST=California, L=San Francisco, O=Fastly, Inc., CN=r2.shared.global.fastly.net', + name: 'tls.detailed.client_certificate.issuer.distinguished_name', + type: 'keyword', + }, + 'tls.detailed.server_certificate.version': { + category: 'tls', + description: 'X509 format version.', + name: 'tls.detailed.server_certificate.version', + type: 'long', + }, + 'tls.detailed.server_certificate.version_number': { + category: 'tls', + description: 'Version of x509 format.', + example: 3, + name: 'tls.detailed.server_certificate.version_number', + type: 'keyword', + }, + 'tls.detailed.server_certificate.serial_number': { + category: 'tls', + description: "The certificate's serial number.", + name: 'tls.detailed.server_certificate.serial_number', + type: 'keyword', + }, + 'tls.detailed.server_certificate.not_before': { + category: 'tls', + description: 'Date before which the certificate is not valid.', + name: 'tls.detailed.server_certificate.not_before', + type: 'date', + }, + 'tls.detailed.server_certificate.not_after': { + category: 'tls', + description: 'Date after which the certificate expires.', + name: 'tls.detailed.server_certificate.not_after', + type: 'date', + }, + 'tls.detailed.server_certificate.public_key_algorithm': { + category: 'tls', + description: "The algorithm used for this certificate's public key. One of RSA, DSA or ECDSA. ", + name: 'tls.detailed.server_certificate.public_key_algorithm', + type: 'keyword', + }, + 'tls.detailed.server_certificate.public_key_size': { + category: 'tls', + description: 'Size of the public key.', + name: 'tls.detailed.server_certificate.public_key_size', + type: 'long', + }, + 'tls.detailed.server_certificate.signature_algorithm': { + category: 'tls', + description: "The algorithm used for the certificate's signature. ", + name: 'tls.detailed.server_certificate.signature_algorithm', + type: 'keyword', + }, + 'tls.detailed.server_certificate.alternative_names': { + category: 'tls', + description: 'Subject Alternative Names for this certificate.', + name: 'tls.detailed.server_certificate.alternative_names', + type: 'keyword', + }, + 'tls.detailed.server_certificate.subject.country': { + category: 'tls', + description: 'Country code.', + name: 'tls.detailed.server_certificate.subject.country', + type: 'keyword', + }, + 'tls.detailed.server_certificate.subject.organization': { + category: 'tls', + description: 'Organization name.', + name: 'tls.detailed.server_certificate.subject.organization', + type: 'keyword', + }, + 'tls.detailed.server_certificate.subject.organizational_unit': { + category: 'tls', + description: 'Unit within organization.', + name: 'tls.detailed.server_certificate.subject.organizational_unit', + type: 'keyword', + }, + 'tls.detailed.server_certificate.subject.province': { + category: 'tls', + description: 'Province or region within country.', + name: 'tls.detailed.server_certificate.subject.province', + type: 'keyword', + }, + 'tls.detailed.server_certificate.subject.state_or_province': { + category: 'tls', + description: 'Province or region within country.', + name: 'tls.detailed.server_certificate.subject.state_or_province', + type: 'keyword', + }, + 'tls.detailed.server_certificate.subject.common_name': { + category: 'tls', + description: 'Name or host name identified by the certificate.', + name: 'tls.detailed.server_certificate.subject.common_name', + type: 'keyword', + }, + 'tls.detailed.server_certificate.subject.locality': { + category: 'tls', + description: 'Locality.', + name: 'tls.detailed.server_certificate.subject.locality', + type: 'keyword', + }, + 'tls.detailed.server_certificate.subject.distinguished_name': { + category: 'tls', + description: 'Distinguished name (DN) of the certificate subject entity.', + example: 'C=US, ST=California, L=San Francisco, O=Fastly, Inc., CN=r2.shared.global.fastly.net', + name: 'tls.detailed.server_certificate.subject.distinguished_name', + type: 'keyword', + }, + 'tls.detailed.server_certificate.issuer.country': { + category: 'tls', + description: 'Country code.', + name: 'tls.detailed.server_certificate.issuer.country', + type: 'keyword', + }, + 'tls.detailed.server_certificate.issuer.organization': { + category: 'tls', + description: 'Organization name.', + name: 'tls.detailed.server_certificate.issuer.organization', + type: 'keyword', + }, + 'tls.detailed.server_certificate.issuer.organizational_unit': { + category: 'tls', + description: 'Unit within organization.', + name: 'tls.detailed.server_certificate.issuer.organizational_unit', + type: 'keyword', + }, + 'tls.detailed.server_certificate.issuer.province': { + category: 'tls', + description: 'Province or region within country.', + name: 'tls.detailed.server_certificate.issuer.province', + type: 'keyword', + }, + 'tls.detailed.server_certificate.issuer.state_or_province': { + category: 'tls', + description: 'Province or region within country.', + name: 'tls.detailed.server_certificate.issuer.state_or_province', + type: 'keyword', + }, + 'tls.detailed.server_certificate.issuer.common_name': { + category: 'tls', + description: 'Name or host name identified by the certificate.', + name: 'tls.detailed.server_certificate.issuer.common_name', + type: 'keyword', + }, + 'tls.detailed.server_certificate.issuer.locality': { + category: 'tls', + description: 'Locality.', + name: 'tls.detailed.server_certificate.issuer.locality', + type: 'keyword', + }, + 'tls.detailed.server_certificate.issuer.distinguished_name': { + category: 'tls', + description: 'Distinguished name (DN) of the certificate issuer entity.', + example: 'C=US, ST=California, L=San Francisco, O=Fastly, Inc., CN=r2.shared.global.fastly.net', + name: 'tls.detailed.server_certificate.issuer.distinguished_name', + type: 'keyword', + }, + 'tls.detailed.server_certificate_chain': { + category: 'tls', + description: 'Chain of trust for the server certificate.', + name: 'tls.detailed.server_certificate_chain', + type: 'array', + }, + 'tls.detailed.client_certificate_chain': { + category: 'tls', + description: 'Chain of trust for the client certificate.', + name: 'tls.detailed.client_certificate_chain', + type: 'array', + }, + 'tls.detailed.alert_types': { + category: 'tls', + description: 'An array containing the TLS alert type for every alert received. ', + name: 'tls.detailed.alert_types', + type: 'keyword', + }, + 'tls.handshake_completed': { + category: 'tls', + name: 'tls.handshake_completed', + type: 'alias', + }, + 'tls.client_hello.supported_ciphers': { + category: 'tls', + name: 'tls.client_hello.supported_ciphers', + type: 'alias', + }, + 'tls.server_hello.selected_cipher': { + category: 'tls', + name: 'tls.server_hello.selected_cipher', + type: 'alias', + }, + 'tls.fingerprints.ja3': { + category: 'tls', + name: 'tls.fingerprints.ja3', + type: 'alias', + }, + 'tls.resumption_method': { + category: 'tls', + name: 'tls.resumption_method', + type: 'alias', + }, + 'tls.client_certificate_requested': { + category: 'tls', + name: 'tls.client_certificate_requested', + type: 'alias', + }, + 'tls.client_hello.version': { + category: 'tls', + name: 'tls.client_hello.version', + type: 'alias', + }, + 'tls.client_hello.session_id': { + category: 'tls', + name: 'tls.client_hello.session_id', + type: 'alias', + }, + 'tls.client_hello.supported_compression_methods': { + category: 'tls', + name: 'tls.client_hello.supported_compression_methods', + type: 'alias', + }, + 'tls.client_hello.extensions.server_name_indication': { + category: 'tls', + name: 'tls.client_hello.extensions.server_name_indication', + type: 'alias', + }, + 'tls.client_hello.extensions.application_layer_protocol_negotiation': { + category: 'tls', + name: 'tls.client_hello.extensions.application_layer_protocol_negotiation', + type: 'alias', + }, + 'tls.client_hello.extensions.session_ticket': { + category: 'tls', + name: 'tls.client_hello.extensions.session_ticket', + type: 'alias', + }, + 'tls.client_hello.extensions.supported_versions': { + category: 'tls', + name: 'tls.client_hello.extensions.supported_versions', + type: 'alias', + }, + 'tls.client_hello.extensions.supported_groups': { + category: 'tls', + name: 'tls.client_hello.extensions.supported_groups', + type: 'alias', + }, + 'tls.client_hello.extensions.signature_algorithms': { + category: 'tls', + name: 'tls.client_hello.extensions.signature_algorithms', + type: 'alias', + }, + 'tls.client_hello.extensions.ec_points_formats': { + category: 'tls', + name: 'tls.client_hello.extensions.ec_points_formats', + type: 'alias', + }, + 'tls.client_hello.extensions._unparsed_': { + category: 'tls', + name: 'tls.client_hello.extensions._unparsed_', + type: 'alias', + }, + 'tls.server_hello.version': { + category: 'tls', + name: 'tls.server_hello.version', + type: 'alias', + }, + 'tls.server_hello.selected_compression_method': { + category: 'tls', + name: 'tls.server_hello.selected_compression_method', + type: 'alias', + }, + 'tls.server_hello.session_id': { + category: 'tls', + name: 'tls.server_hello.session_id', + type: 'alias', + }, + 'tls.server_hello.extensions.application_layer_protocol_negotiation': { + category: 'tls', + name: 'tls.server_hello.extensions.application_layer_protocol_negotiation', + type: 'alias', + }, + 'tls.server_hello.extensions.session_ticket': { + category: 'tls', + name: 'tls.server_hello.extensions.session_ticket', + type: 'alias', + }, + 'tls.server_hello.extensions.supported_versions': { + category: 'tls', + name: 'tls.server_hello.extensions.supported_versions', + type: 'alias', + }, + 'tls.server_hello.extensions.ec_points_formats': { + category: 'tls', + name: 'tls.server_hello.extensions.ec_points_formats', + type: 'alias', + }, + 'tls.server_hello.extensions._unparsed_': { + category: 'tls', + name: 'tls.server_hello.extensions._unparsed_', + type: 'alias', + }, + 'tls.client_certificate.version': { + category: 'tls', + name: 'tls.client_certificate.version', + type: 'alias', + }, + 'tls.client_certificate.serial_number': { + category: 'tls', + name: 'tls.client_certificate.serial_number', + type: 'alias', + }, + 'tls.client_certificate.not_before': { + category: 'tls', + name: 'tls.client_certificate.not_before', + type: 'alias', + }, + 'tls.client_certificate.not_after': { + category: 'tls', + name: 'tls.client_certificate.not_after', + type: 'alias', + }, + 'tls.client_certificate.public_key_algorithm': { + category: 'tls', + name: 'tls.client_certificate.public_key_algorithm', + type: 'alias', + }, + 'tls.client_certificate.public_key_size': { + category: 'tls', + name: 'tls.client_certificate.public_key_size', + type: 'alias', + }, + 'tls.client_certificate.signature_algorithm': { + category: 'tls', + name: 'tls.client_certificate.signature_algorithm', + type: 'alias', + }, + 'tls.client_certificate.alternative_names': { + category: 'tls', + name: 'tls.client_certificate.alternative_names', + type: 'alias', + }, + 'tls.client_certificate.subject.country': { + category: 'tls', + name: 'tls.client_certificate.subject.country', + type: 'alias', + }, + 'tls.client_certificate.subject.organization': { + category: 'tls', + name: 'tls.client_certificate.subject.organization', + type: 'alias', + }, + 'tls.client_certificate.subject.organizational_unit': { + category: 'tls', + name: 'tls.client_certificate.subject.organizational_unit', + type: 'alias', + }, + 'tls.client_certificate.subject.province': { + category: 'tls', + name: 'tls.client_certificate.subject.province', + type: 'alias', + }, + 'tls.client_certificate.subject.common_name': { + category: 'tls', + name: 'tls.client_certificate.subject.common_name', + type: 'alias', + }, + 'tls.client_certificate.subject.locality': { + category: 'tls', + name: 'tls.client_certificate.subject.locality', + type: 'alias', + }, + 'tls.client_certificate.issuer.country': { + category: 'tls', + name: 'tls.client_certificate.issuer.country', + type: 'alias', + }, + 'tls.client_certificate.issuer.organization': { + category: 'tls', + name: 'tls.client_certificate.issuer.organization', + type: 'alias', + }, + 'tls.client_certificate.issuer.organizational_unit': { + category: 'tls', + name: 'tls.client_certificate.issuer.organizational_unit', + type: 'alias', + }, + 'tls.client_certificate.issuer.province': { + category: 'tls', + name: 'tls.client_certificate.issuer.province', + type: 'alias', + }, + 'tls.client_certificate.issuer.common_name': { + category: 'tls', + name: 'tls.client_certificate.issuer.common_name', + type: 'alias', + }, + 'tls.client_certificate.issuer.locality': { + category: 'tls', + name: 'tls.client_certificate.issuer.locality', + type: 'alias', + }, + 'tls.server_certificate.version': { + category: 'tls', + name: 'tls.server_certificate.version', + type: 'alias', + }, + 'tls.server_certificate.serial_number': { + category: 'tls', + name: 'tls.server_certificate.serial_number', + type: 'alias', + }, + 'tls.server_certificate.not_before': { + category: 'tls', + name: 'tls.server_certificate.not_before', + type: 'alias', + }, + 'tls.server_certificate.not_after': { + category: 'tls', + name: 'tls.server_certificate.not_after', + type: 'alias', + }, + 'tls.server_certificate.public_key_algorithm': { + category: 'tls', + name: 'tls.server_certificate.public_key_algorithm', + type: 'alias', + }, + 'tls.server_certificate.public_key_size': { + category: 'tls', + name: 'tls.server_certificate.public_key_size', + type: 'alias', + }, + 'tls.server_certificate.signature_algorithm': { + category: 'tls', + name: 'tls.server_certificate.signature_algorithm', + type: 'alias', + }, + 'tls.server_certificate.alternative_names': { + category: 'tls', + name: 'tls.server_certificate.alternative_names', + type: 'alias', + }, + 'tls.server_certificate.subject.country': { + category: 'tls', + name: 'tls.server_certificate.subject.country', + type: 'alias', + }, + 'tls.server_certificate.subject.organization': { + category: 'tls', + name: 'tls.server_certificate.subject.organization', + type: 'alias', + }, + 'tls.server_certificate.subject.organizational_unit': { + category: 'tls', + name: 'tls.server_certificate.subject.organizational_unit', + type: 'alias', + }, + 'tls.server_certificate.subject.province': { + category: 'tls', + name: 'tls.server_certificate.subject.province', + type: 'alias', + }, + 'tls.server_certificate.subject.common_name': { + category: 'tls', + name: 'tls.server_certificate.subject.common_name', + type: 'alias', + }, + 'tls.server_certificate.subject.locality': { + category: 'tls', + name: 'tls.server_certificate.subject.locality', + type: 'alias', + }, + 'tls.server_certificate.issuer.country': { + category: 'tls', + name: 'tls.server_certificate.issuer.country', + type: 'alias', + }, + 'tls.server_certificate.issuer.organization': { + category: 'tls', + name: 'tls.server_certificate.issuer.organization', + type: 'alias', + }, + 'tls.server_certificate.issuer.organizational_unit': { + category: 'tls', + name: 'tls.server_certificate.issuer.organizational_unit', + type: 'alias', + }, + 'tls.server_certificate.issuer.province': { + category: 'tls', + name: 'tls.server_certificate.issuer.province', + type: 'alias', + }, + 'tls.server_certificate.issuer.common_name': { + category: 'tls', + name: 'tls.server_certificate.issuer.common_name', + type: 'alias', + }, + 'tls.server_certificate.issuer.locality': { + category: 'tls', + name: 'tls.server_certificate.issuer.locality', + type: 'alias', + }, + 'tls.alert_types': { + category: 'tls', + name: 'tls.alert_types', + type: 'alias', + }, + 'winlog.api': { + category: 'winlog', + description: + 'The event log API type used to read the record. The possible values are "wineventlog" for the Windows Event Log API or "eventlogging" for the Event Logging API. The Event Logging API was designed for Windows Server 2003 or Windows 2000 operating systems. In Windows Vista, the event logging infrastructure was redesigned. On Windows Vista or later operating systems, the Windows Event Log API is used. Winlogbeat automatically detects which API to use for reading event logs. ', + name: 'winlog.api', + }, + 'winlog.activity_id': { + category: 'winlog', + description: + 'A globally unique identifier that identifies the current activity. The events that are published with this identifier are part of the same activity. ', + name: 'winlog.activity_id', + type: 'keyword', + }, + 'winlog.computer_name': { + category: 'winlog', + description: + 'The name of the computer that generated the record. When using Windows event forwarding, this name can differ from `agent.hostname`. ', + name: 'winlog.computer_name', + type: 'keyword', + }, + 'winlog.event_data': { + category: 'winlog', + description: + 'The event-specific data. This field is mutually exclusive with `user_data`. If you are capturing event data on versions prior to Windows Vista, the parameters in `event_data` are named `param1`, `param2`, and so on, because event log parameters are unnamed in earlier versions of Windows. ', + name: 'winlog.event_data', + type: 'object', + }, + 'winlog.event_data.AuthenticationPackageName': { + category: 'winlog', + name: 'winlog.event_data.AuthenticationPackageName', + type: 'keyword', + }, + 'winlog.event_data.Binary': { + category: 'winlog', + name: 'winlog.event_data.Binary', + type: 'keyword', + }, + 'winlog.event_data.BitlockerUserInputTime': { + category: 'winlog', + name: 'winlog.event_data.BitlockerUserInputTime', + type: 'keyword', + }, + 'winlog.event_data.BootMode': { + category: 'winlog', + name: 'winlog.event_data.BootMode', + type: 'keyword', + }, + 'winlog.event_data.BootType': { + category: 'winlog', + name: 'winlog.event_data.BootType', + type: 'keyword', + }, + 'winlog.event_data.BuildVersion': { + category: 'winlog', + name: 'winlog.event_data.BuildVersion', + type: 'keyword', + }, + 'winlog.event_data.Company': { + category: 'winlog', + name: 'winlog.event_data.Company', + type: 'keyword', + }, + 'winlog.event_data.CorruptionActionState': { + category: 'winlog', + name: 'winlog.event_data.CorruptionActionState', + type: 'keyword', + }, + 'winlog.event_data.CreationUtcTime': { + category: 'winlog', + name: 'winlog.event_data.CreationUtcTime', + type: 'keyword', + }, + 'winlog.event_data.Description': { + category: 'winlog', + name: 'winlog.event_data.Description', + type: 'keyword', + }, + 'winlog.event_data.Detail': { + category: 'winlog', + name: 'winlog.event_data.Detail', + type: 'keyword', + }, + 'winlog.event_data.DeviceName': { + category: 'winlog', + name: 'winlog.event_data.DeviceName', + type: 'keyword', + }, + 'winlog.event_data.DeviceNameLength': { + category: 'winlog', + name: 'winlog.event_data.DeviceNameLength', + type: 'keyword', + }, + 'winlog.event_data.DeviceTime': { + category: 'winlog', + name: 'winlog.event_data.DeviceTime', + type: 'keyword', + }, + 'winlog.event_data.DeviceVersionMajor': { + category: 'winlog', + name: 'winlog.event_data.DeviceVersionMajor', + type: 'keyword', + }, + 'winlog.event_data.DeviceVersionMinor': { + category: 'winlog', + name: 'winlog.event_data.DeviceVersionMinor', + type: 'keyword', + }, + 'winlog.event_data.DriveName': { + category: 'winlog', + name: 'winlog.event_data.DriveName', + type: 'keyword', + }, + 'winlog.event_data.DriverName': { + category: 'winlog', + name: 'winlog.event_data.DriverName', + type: 'keyword', + }, + 'winlog.event_data.DriverNameLength': { + category: 'winlog', + name: 'winlog.event_data.DriverNameLength', + type: 'keyword', + }, + 'winlog.event_data.DwordVal': { + category: 'winlog', + name: 'winlog.event_data.DwordVal', + type: 'keyword', + }, + 'winlog.event_data.EntryCount': { + category: 'winlog', + name: 'winlog.event_data.EntryCount', + type: 'keyword', + }, + 'winlog.event_data.ExtraInfo': { + category: 'winlog', + name: 'winlog.event_data.ExtraInfo', + type: 'keyword', + }, + 'winlog.event_data.FailureName': { + category: 'winlog', + name: 'winlog.event_data.FailureName', + type: 'keyword', + }, + 'winlog.event_data.FailureNameLength': { + category: 'winlog', + name: 'winlog.event_data.FailureNameLength', + type: 'keyword', + }, + 'winlog.event_data.FileVersion': { + category: 'winlog', + name: 'winlog.event_data.FileVersion', + type: 'keyword', + }, + 'winlog.event_data.FinalStatus': { + category: 'winlog', + name: 'winlog.event_data.FinalStatus', + type: 'keyword', + }, + 'winlog.event_data.Group': { + category: 'winlog', + name: 'winlog.event_data.Group', + type: 'keyword', + }, + 'winlog.event_data.IdleImplementation': { + category: 'winlog', + name: 'winlog.event_data.IdleImplementation', + type: 'keyword', + }, + 'winlog.event_data.IdleStateCount': { + category: 'winlog', + name: 'winlog.event_data.IdleStateCount', + type: 'keyword', + }, + 'winlog.event_data.ImpersonationLevel': { + category: 'winlog', + name: 'winlog.event_data.ImpersonationLevel', + type: 'keyword', + }, + 'winlog.event_data.IntegrityLevel': { + category: 'winlog', + name: 'winlog.event_data.IntegrityLevel', + type: 'keyword', + }, + 'winlog.event_data.IpAddress': { + category: 'winlog', + name: 'winlog.event_data.IpAddress', + type: 'keyword', + }, + 'winlog.event_data.IpPort': { + category: 'winlog', + name: 'winlog.event_data.IpPort', + type: 'keyword', + }, + 'winlog.event_data.KeyLength': { + category: 'winlog', + name: 'winlog.event_data.KeyLength', + type: 'keyword', + }, + 'winlog.event_data.LastBootGood': { + category: 'winlog', + name: 'winlog.event_data.LastBootGood', + type: 'keyword', + }, + 'winlog.event_data.LastShutdownGood': { + category: 'winlog', + name: 'winlog.event_data.LastShutdownGood', + type: 'keyword', + }, + 'winlog.event_data.LmPackageName': { + category: 'winlog', + name: 'winlog.event_data.LmPackageName', + type: 'keyword', + }, + 'winlog.event_data.LogonGuid': { + category: 'winlog', + name: 'winlog.event_data.LogonGuid', + type: 'keyword', + }, + 'winlog.event_data.LogonId': { + category: 'winlog', + name: 'winlog.event_data.LogonId', + type: 'keyword', + }, + 'winlog.event_data.LogonProcessName': { + category: 'winlog', + name: 'winlog.event_data.LogonProcessName', + type: 'keyword', + }, + 'winlog.event_data.LogonType': { + category: 'winlog', + name: 'winlog.event_data.LogonType', + type: 'keyword', + }, + 'winlog.event_data.MajorVersion': { + category: 'winlog', + name: 'winlog.event_data.MajorVersion', + type: 'keyword', + }, + 'winlog.event_data.MaximumPerformancePercent': { + category: 'winlog', + name: 'winlog.event_data.MaximumPerformancePercent', + type: 'keyword', + }, + 'winlog.event_data.MemberName': { + category: 'winlog', + name: 'winlog.event_data.MemberName', + type: 'keyword', + }, + 'winlog.event_data.MemberSid': { + category: 'winlog', + name: 'winlog.event_data.MemberSid', + type: 'keyword', + }, + 'winlog.event_data.MinimumPerformancePercent': { + category: 'winlog', + name: 'winlog.event_data.MinimumPerformancePercent', + type: 'keyword', + }, + 'winlog.event_data.MinimumThrottlePercent': { + category: 'winlog', + name: 'winlog.event_data.MinimumThrottlePercent', + type: 'keyword', + }, + 'winlog.event_data.MinorVersion': { + category: 'winlog', + name: 'winlog.event_data.MinorVersion', + type: 'keyword', + }, + 'winlog.event_data.NewProcessId': { + category: 'winlog', + name: 'winlog.event_data.NewProcessId', + type: 'keyword', + }, + 'winlog.event_data.NewProcessName': { + category: 'winlog', + name: 'winlog.event_data.NewProcessName', + type: 'keyword', + }, + 'winlog.event_data.NewSchemeGuid': { + category: 'winlog', + name: 'winlog.event_data.NewSchemeGuid', + type: 'keyword', + }, + 'winlog.event_data.NewTime': { + category: 'winlog', + name: 'winlog.event_data.NewTime', + type: 'keyword', + }, + 'winlog.event_data.NominalFrequency': { + category: 'winlog', + name: 'winlog.event_data.NominalFrequency', + type: 'keyword', + }, + 'winlog.event_data.Number': { + category: 'winlog', + name: 'winlog.event_data.Number', + type: 'keyword', + }, + 'winlog.event_data.OldSchemeGuid': { + category: 'winlog', + name: 'winlog.event_data.OldSchemeGuid', + type: 'keyword', + }, + 'winlog.event_data.OldTime': { + category: 'winlog', + name: 'winlog.event_data.OldTime', + type: 'keyword', + }, + 'winlog.event_data.OriginalFileName': { + category: 'winlog', + name: 'winlog.event_data.OriginalFileName', + type: 'keyword', + }, + 'winlog.event_data.Path': { + category: 'winlog', + name: 'winlog.event_data.Path', + type: 'keyword', + }, + 'winlog.event_data.PerformanceImplementation': { + category: 'winlog', + name: 'winlog.event_data.PerformanceImplementation', + type: 'keyword', + }, + 'winlog.event_data.PreviousCreationUtcTime': { + category: 'winlog', + name: 'winlog.event_data.PreviousCreationUtcTime', + type: 'keyword', + }, + 'winlog.event_data.PreviousTime': { + category: 'winlog', + name: 'winlog.event_data.PreviousTime', + type: 'keyword', + }, + 'winlog.event_data.PrivilegeList': { + category: 'winlog', + name: 'winlog.event_data.PrivilegeList', + type: 'keyword', + }, + 'winlog.event_data.ProcessId': { + category: 'winlog', + name: 'winlog.event_data.ProcessId', + type: 'keyword', + }, + 'winlog.event_data.ProcessName': { + category: 'winlog', + name: 'winlog.event_data.ProcessName', + type: 'keyword', + }, + 'winlog.event_data.ProcessPath': { + category: 'winlog', + name: 'winlog.event_data.ProcessPath', + type: 'keyword', + }, + 'winlog.event_data.ProcessPid': { + category: 'winlog', + name: 'winlog.event_data.ProcessPid', + type: 'keyword', + }, + 'winlog.event_data.Product': { + category: 'winlog', + name: 'winlog.event_data.Product', + type: 'keyword', + }, + 'winlog.event_data.PuaCount': { + category: 'winlog', + name: 'winlog.event_data.PuaCount', + type: 'keyword', + }, + 'winlog.event_data.PuaPolicyId': { + category: 'winlog', + name: 'winlog.event_data.PuaPolicyId', + type: 'keyword', + }, + 'winlog.event_data.QfeVersion': { + category: 'winlog', + name: 'winlog.event_data.QfeVersion', + type: 'keyword', + }, + 'winlog.event_data.Reason': { + category: 'winlog', + name: 'winlog.event_data.Reason', + type: 'keyword', + }, + 'winlog.event_data.SchemaVersion': { + category: 'winlog', + name: 'winlog.event_data.SchemaVersion', + type: 'keyword', + }, + 'winlog.event_data.ScriptBlockText': { + category: 'winlog', + name: 'winlog.event_data.ScriptBlockText', + type: 'keyword', + }, + 'winlog.event_data.ServiceName': { + category: 'winlog', + name: 'winlog.event_data.ServiceName', + type: 'keyword', + }, + 'winlog.event_data.ServiceVersion': { + category: 'winlog', + name: 'winlog.event_data.ServiceVersion', + type: 'keyword', + }, + 'winlog.event_data.ShutdownActionType': { + category: 'winlog', + name: 'winlog.event_data.ShutdownActionType', + type: 'keyword', + }, + 'winlog.event_data.ShutdownEventCode': { + category: 'winlog', + name: 'winlog.event_data.ShutdownEventCode', + type: 'keyword', + }, + 'winlog.event_data.ShutdownReason': { + category: 'winlog', + name: 'winlog.event_data.ShutdownReason', + type: 'keyword', + }, + 'winlog.event_data.Signature': { + category: 'winlog', + name: 'winlog.event_data.Signature', + type: 'keyword', + }, + 'winlog.event_data.SignatureStatus': { + category: 'winlog', + name: 'winlog.event_data.SignatureStatus', + type: 'keyword', + }, + 'winlog.event_data.Signed': { + category: 'winlog', + name: 'winlog.event_data.Signed', + type: 'keyword', + }, + 'winlog.event_data.StartTime': { + category: 'winlog', + name: 'winlog.event_data.StartTime', + type: 'keyword', + }, + 'winlog.event_data.State': { + category: 'winlog', + name: 'winlog.event_data.State', + type: 'keyword', + }, + 'winlog.event_data.Status': { + category: 'winlog', + name: 'winlog.event_data.Status', + type: 'keyword', + }, + 'winlog.event_data.StopTime': { + category: 'winlog', + name: 'winlog.event_data.StopTime', + type: 'keyword', + }, + 'winlog.event_data.SubjectDomainName': { + category: 'winlog', + name: 'winlog.event_data.SubjectDomainName', + type: 'keyword', + }, + 'winlog.event_data.SubjectLogonId': { + category: 'winlog', + name: 'winlog.event_data.SubjectLogonId', + type: 'keyword', + }, + 'winlog.event_data.SubjectUserName': { + category: 'winlog', + name: 'winlog.event_data.SubjectUserName', + type: 'keyword', + }, + 'winlog.event_data.SubjectUserSid': { + category: 'winlog', + name: 'winlog.event_data.SubjectUserSid', + type: 'keyword', + }, + 'winlog.event_data.TSId': { + category: 'winlog', + name: 'winlog.event_data.TSId', + type: 'keyword', + }, + 'winlog.event_data.TargetDomainName': { + category: 'winlog', + name: 'winlog.event_data.TargetDomainName', + type: 'keyword', + }, + 'winlog.event_data.TargetInfo': { + category: 'winlog', + name: 'winlog.event_data.TargetInfo', + type: 'keyword', + }, + 'winlog.event_data.TargetLogonGuid': { + category: 'winlog', + name: 'winlog.event_data.TargetLogonGuid', + type: 'keyword', + }, + 'winlog.event_data.TargetLogonId': { + category: 'winlog', + name: 'winlog.event_data.TargetLogonId', + type: 'keyword', + }, + 'winlog.event_data.TargetServerName': { + category: 'winlog', + name: 'winlog.event_data.TargetServerName', + type: 'keyword', + }, + 'winlog.event_data.TargetUserName': { + category: 'winlog', + name: 'winlog.event_data.TargetUserName', + type: 'keyword', + }, + 'winlog.event_data.TargetUserSid': { + category: 'winlog', + name: 'winlog.event_data.TargetUserSid', + type: 'keyword', + }, + 'winlog.event_data.TerminalSessionId': { + category: 'winlog', + name: 'winlog.event_data.TerminalSessionId', + type: 'keyword', + }, + 'winlog.event_data.TokenElevationType': { + category: 'winlog', + name: 'winlog.event_data.TokenElevationType', + type: 'keyword', + }, + 'winlog.event_data.TransmittedServices': { + category: 'winlog', + name: 'winlog.event_data.TransmittedServices', + type: 'keyword', + }, + 'winlog.event_data.UserSid': { + category: 'winlog', + name: 'winlog.event_data.UserSid', + type: 'keyword', + }, + 'winlog.event_data.Version': { + category: 'winlog', + name: 'winlog.event_data.Version', + type: 'keyword', + }, + 'winlog.event_data.Workstation': { + category: 'winlog', + name: 'winlog.event_data.Workstation', + type: 'keyword', + }, + 'winlog.event_data.param1': { + category: 'winlog', + name: 'winlog.event_data.param1', + type: 'keyword', + }, + 'winlog.event_data.param2': { + category: 'winlog', + name: 'winlog.event_data.param2', + type: 'keyword', + }, + 'winlog.event_data.param3': { + category: 'winlog', + name: 'winlog.event_data.param3', + type: 'keyword', + }, + 'winlog.event_data.param4': { + category: 'winlog', + name: 'winlog.event_data.param4', + type: 'keyword', + }, + 'winlog.event_data.param5': { + category: 'winlog', + name: 'winlog.event_data.param5', + type: 'keyword', + }, + 'winlog.event_data.param6': { + category: 'winlog', + name: 'winlog.event_data.param6', + type: 'keyword', + }, + 'winlog.event_data.param7': { + category: 'winlog', + name: 'winlog.event_data.param7', + type: 'keyword', + }, + 'winlog.event_data.param8': { + category: 'winlog', + name: 'winlog.event_data.param8', + type: 'keyword', + }, + 'winlog.event_id': { + category: 'winlog', + description: 'The event identifier. The value is specific to the source of the event. ', + name: 'winlog.event_id', + type: 'keyword', + }, + 'winlog.keywords': { + category: 'winlog', + description: 'The keywords are used to classify an event. ', + name: 'winlog.keywords', + type: 'keyword', + }, + 'winlog.channel': { + category: 'winlog', + description: + 'The name of the channel from which this record was read. This value is one of the names from the `event_logs` collection in the configuration. ', + name: 'winlog.channel', + type: 'keyword', + }, + 'winlog.record_id': { + category: 'winlog', + description: + 'The record ID of the event log record. The first record written to an event log is record number 1, and other records are numbered sequentially. If the record number reaches the maximum value (2^32^ for the Event Logging API and 2^64^ for the Windows Event Log API), the next record number will be 0. ', + name: 'winlog.record_id', + type: 'keyword', + }, + 'winlog.related_activity_id': { + category: 'winlog', + description: + 'A globally unique identifier that identifies the activity to which control was transferred to. The related events would then have this identifier as their `activity_id` identifier. ', + name: 'winlog.related_activity_id', + type: 'keyword', + }, + 'winlog.opcode': { + category: 'winlog', + description: + 'The opcode defined in the event. Task and opcode are typically used to identify the location in the application from where the event was logged. ', + name: 'winlog.opcode', + type: 'keyword', + }, + 'winlog.provider_guid': { + category: 'winlog', + description: + 'A globally unique identifier that identifies the provider that logged the event. ', + name: 'winlog.provider_guid', + type: 'keyword', + }, + 'winlog.process.pid': { + category: 'winlog', + description: 'The process_id of the Client Server Runtime Process. ', + name: 'winlog.process.pid', + type: 'long', + }, + 'winlog.provider_name': { + category: 'winlog', + description: + 'The source of the event log record (the application or service that logged the record). ', + name: 'winlog.provider_name', + type: 'keyword', + }, + 'winlog.task': { + category: 'winlog', + description: + 'The task defined in the event. Task and opcode are typically used to identify the location in the application from where the event was logged. The category used by the Event Logging API (on pre Windows Vista operating systems) is written to this field. ', + name: 'winlog.task', + type: 'keyword', + }, + 'winlog.process.thread.id': { + category: 'winlog', + name: 'winlog.process.thread.id', + type: 'long', + }, + 'winlog.user_data': { + category: 'winlog', + description: 'The event specific data. This field is mutually exclusive with `event_data`. ', + name: 'winlog.user_data', + type: 'object', + }, + 'winlog.user.identifier': { + category: 'winlog', + description: + 'The Windows security identifier (SID) of the account associated with this event. If Winlogbeat cannot resolve the SID to a name, then the `user.name`, `user.domain`, and `user.type` fields will be omitted from the event. If you discover Winlogbeat not resolving SIDs, review the log for clues as to what the problem may be. ', + example: 'S-1-5-21-3541430928-2051711210-1391384369-1001', + name: 'winlog.user.identifier', + type: 'keyword', + }, + 'winlog.user.name': { + category: 'winlog', + description: 'Name of the user associated with this event. ', + name: 'winlog.user.name', + type: 'keyword', + }, + 'winlog.user.domain': { + category: 'winlog', + description: 'The domain that the account associated with this event is a member of. ', + name: 'winlog.user.domain', + type: 'keyword', + }, + 'winlog.user.type': { + category: 'winlog', + description: 'The type of account associated with this event. ', + name: 'winlog.user.type', + type: 'keyword', + }, + 'winlog.version': { + category: 'winlog', + description: "The version number of the event's definition.", + name: 'winlog.version', + type: 'long', + }, + activity_id: { + category: 'base', + name: 'activity_id', + type: 'alias', + }, + computer_name: { + category: 'base', + name: 'computer_name', + type: 'alias', + }, + event_id: { + category: 'base', + name: 'event_id', + type: 'alias', + }, + keywords: { + category: 'base', + name: 'keywords', + type: 'alias', + }, + log_name: { + category: 'base', + name: 'log_name', + type: 'alias', + }, + message_error: { + category: 'base', + name: 'message_error', + type: 'alias', + }, + record_number: { + category: 'base', + name: 'record_number', + type: 'alias', + }, + related_activity_id: { + category: 'base', + name: 'related_activity_id', + type: 'alias', + }, + opcode: { + category: 'base', + name: 'opcode', + type: 'alias', + }, + provider_guid: { + category: 'base', + name: 'provider_guid', + type: 'alias', + }, + process_id: { + category: 'base', + name: 'process_id', + type: 'alias', + }, + source_name: { + category: 'base', + name: 'source_name', + type: 'alias', + }, + task: { + category: 'base', + name: 'task', + type: 'alias', + }, + thread_id: { + category: 'base', + name: 'thread_id', + type: 'alias', + }, + 'user.identifier': { + category: 'user', + name: 'user.identifier', + type: 'alias', + }, + 'user.type': { + category: 'user', + name: 'user.type', + type: 'alias', + }, + version: { + category: 'base', + name: 'version', + type: 'alias', + }, + xml: { + category: 'base', + name: 'xml', + type: 'alias', + }, + 'powershell.id': { + category: 'powershell', + description: 'Shell Id.', + example: 'Microsoft Powershell', + name: 'powershell.id', + type: 'keyword', + }, + 'powershell.pipeline_id': { + category: 'powershell', + description: 'Pipeline id.', + example: '1', + name: 'powershell.pipeline_id', + type: 'keyword', + }, + 'powershell.runspace_id': { + category: 'powershell', + description: 'Runspace id.', + example: '4fa9074d-45ab-4e53-9195-e91981ac2bbb', + name: 'powershell.runspace_id', + type: 'keyword', + }, + 'powershell.sequence': { + category: 'powershell', + description: 'Sequence number of the powershell execution.', + example: 1, + name: 'powershell.sequence', + type: 'long', + }, + 'powershell.total': { + category: 'powershell', + description: 'Total number of messages in the sequence.', + example: 10, + name: 'powershell.total', + type: 'long', + }, + 'powershell.command.path': { + category: 'powershell', + description: 'Path of the executed command.', + example: 'C:\\Windows\\system32\\cmd.exe', + name: 'powershell.command.path', + type: 'keyword', + }, + 'powershell.command.name': { + category: 'powershell', + description: 'Name of the executed command.', + example: 'cmd.exe', + name: 'powershell.command.name', + type: 'keyword', + }, + 'powershell.command.type': { + category: 'powershell', + description: 'Type of the executed command.', + example: 'Application', + name: 'powershell.command.type', + type: 'keyword', + }, + 'powershell.command.value': { + category: 'powershell', + description: 'The invoked command.', + example: 'Import-LocalizedData LocalizedData -filename ArchiveResources', + name: 'powershell.command.value', + type: 'text', + }, + 'powershell.command.invocation_details': { + category: 'powershell', + description: 'An array of objects containing detailed information of the executed command. ', + name: 'powershell.command.invocation_details', + type: 'array', + }, + 'powershell.command.invocation_details.type': { + category: 'powershell', + description: 'The type of detail.', + example: 'CommandInvocation', + name: 'powershell.command.invocation_details.type', + type: 'keyword', + }, + 'powershell.command.invocation_details.related_command': { + category: 'powershell', + description: 'The command to which the detail is related to.', + example: 'Add-Type', + name: 'powershell.command.invocation_details.related_command', + type: 'keyword', + }, + 'powershell.command.invocation_details.name': { + category: 'powershell', + description: 'Only used for ParameterBinding detail type. Indicates the parameter name. ', + example: 'AssemblyName', + name: 'powershell.command.invocation_details.name', + type: 'keyword', + }, + 'powershell.command.invocation_details.value': { + category: 'powershell', + description: 'The value of the detail. The meaning of it will depend on the detail type. ', + example: 'System.IO.Compression.FileSystem', + name: 'powershell.command.invocation_details.value', + type: 'text', + }, + 'powershell.connected_user.domain': { + category: 'powershell', + description: 'User domain.', + example: 'VAGRANT', + name: 'powershell.connected_user.domain', + type: 'keyword', + }, + 'powershell.connected_user.name': { + category: 'powershell', + description: 'User name.', + example: 'vagrant', + name: 'powershell.connected_user.name', + type: 'keyword', + }, + 'powershell.engine.version': { + category: 'powershell', + description: 'Version of the PowerShell engine version used to execute the command.', + example: '5.1.17763.1007', + name: 'powershell.engine.version', + type: 'keyword', + }, + 'powershell.engine.previous_state': { + category: 'powershell', + description: 'Previous state of the PowerShell engine. ', + example: 'Available', + name: 'powershell.engine.previous_state', + type: 'keyword', + }, + 'powershell.engine.new_state': { + category: 'powershell', + description: 'New state of the PowerShell engine. ', + example: 'Stopped', + name: 'powershell.engine.new_state', + type: 'keyword', + }, + 'powershell.file.script_block_id': { + category: 'powershell', + description: 'Id of the executed script block.', + example: '50d2dbda-7361-4926-a94d-d9eadfdb43fa', + name: 'powershell.file.script_block_id', + type: 'keyword', + }, + 'powershell.file.script_block_text': { + category: 'powershell', + description: 'Text of the executed script block. ', + example: '.\\a_script.ps1', + name: 'powershell.file.script_block_text', + type: 'text', + }, + 'powershell.process.executable_version': { + category: 'powershell', + description: 'Version of the engine hosting process executable.', + example: '5.1.17763.1007', + name: 'powershell.process.executable_version', + type: 'keyword', + }, + 'powershell.provider.new_state': { + category: 'powershell', + description: 'New state of the PowerShell provider. ', + example: 'Active', + name: 'powershell.provider.new_state', + type: 'keyword', + }, + 'powershell.provider.name': { + category: 'powershell', + description: 'Provider name. ', + example: 'Variable', + name: 'powershell.provider.name', + type: 'keyword', + }, + 'winlog.logon.type': { + category: 'winlog', + description: + 'Logon type name. This is the descriptive version of the `winlog.event_data.LogonType` ordinal. This is an enrichment added by the Security module. ', + example: 'RemoteInteractive', + name: 'winlog.logon.type', + type: 'keyword', + }, + 'winlog.logon.id': { + category: 'winlog', + description: + 'Logon ID that can be used to associate this logon with other events related to the same logon session. ', + name: 'winlog.logon.id', + type: 'keyword', + }, + 'winlog.logon.failure.reason': { + category: 'winlog', + description: 'The reason the logon failed. ', + name: 'winlog.logon.failure.reason', + type: 'keyword', + }, + 'winlog.logon.failure.status': { + category: 'winlog', + description: + 'The reason the logon failed. This is textual description based on the value of the hexadecimal `Status` field. ', + name: 'winlog.logon.failure.status', + type: 'keyword', + }, + 'winlog.logon.failure.sub_status': { + category: 'winlog', + description: + 'Additional information about the logon failure. This is a textual description based on the value of the hexidecimal `SubStatus` field. ', + name: 'winlog.logon.failure.sub_status', + type: 'keyword', + }, + 'sysmon.dns.status': { + category: 'sysmon', + description: 'Windows status code returned for the DNS query.', + name: 'sysmon.dns.status', + type: 'keyword', + }, + 'sysmon.file.archived': { + category: 'sysmon', + description: 'Indicates if the deleted file was archived.', + name: 'sysmon.file.archived', + type: 'boolean', + }, + 'sysmon.file.is_executable': { + category: 'sysmon', + description: 'Indicates if the deleted file was an executable.', + name: 'sysmon.file.is_executable', + type: 'boolean', + }, +}; diff --git a/x-pack/plugins/security_solution/server/utils/beat_schema/index.test.ts b/x-pack/plugins/security_solution/server/utils/beat_schema/index.test.ts deleted file mode 100644 index 29944edf382f43..00000000000000 --- a/x-pack/plugins/security_solution/server/utils/beat_schema/index.test.ts +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { cloneDeep, isArray } from 'lodash/fp'; - -import { convertSchemaToAssociativeArray, getIndexSchemaDoc } from '.'; -import { auditbeatSchema, filebeatSchema, packetbeatSchema } from './8.0.0'; -import { Schema } from './type'; - -describe('Schema Beat', () => { - describe('Transform Schema documentation to an associative array', () => { - test('Auditbeat transformation', async () => { - const convertData: Schema = cloneDeep(auditbeatSchema).slice(0, 1); - convertData[0].fields = isArray(convertData[0].fields) - ? convertData[0].fields!.slice(0, 6) - : []; - - expect(convertSchemaToAssociativeArray(convertData)).toEqual({ - '@timestamp': { - description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', - example: '2016-05-23T08:05:34.853Z', - name: '@timestamp', - type: 'date', - }, - labels: { - description: - 'Custom key/value pairs.\n\nCan be used to add meta information to events. Should not contain nested objects.\nAll values are stored as keyword.\n\nExample: `docker` and `k8s` labels.', - example: '{"application": "foo-bar", "env": "production"}', - name: 'labels', - type: 'object', - }, - message: { - description: - 'For log events the message field contains the log message, optimized\nfor viewing in a log viewer.\n\nFor structured logs without an original message field, other fields can be concatenated\nto form a human-readable summary of the event.\n\nIf multiple messages exist, they can be combined into one message.', - example: 'Hello World', - name: 'message', - type: 'text', - }, - tags: { - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - name: 'tags', - type: 'keyword', - }, - agent: { - description: - 'The agent fields contain the data about the software entity, if\nany, that collects, detects, or observes events on a host, or takes measurements\non a host.\n\nExamples include Beats. Agents may also run on observers. ECS agent.* fields\nshall be populated with details of the agent running on the host or observer\nwhere the event happened or the measurement was taken.', - name: 'agent', - type: 'group', - fields: { - 'agent.ephemeral_id': { - description: - 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - name: 'ephemeral_id', - type: 'keyword', - }, - 'agent.id': { - description: - 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', - example: '8a4f500d', - name: 'id', - type: 'keyword', - }, - 'agent.name': { - description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', - example: 'foo', - name: 'name', - type: 'keyword', - }, - 'agent.type': { - description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', - example: 'filebeat', - name: 'type', - type: 'keyword', - }, - 'agent.version': { - description: 'Version of the agent.', - example: '6.0.0-rc2', - name: 'version', - type: 'keyword', - }, - }, - }, - as: { - description: - 'An autonomous system (AS) is a collection of connected Internet Protocol\n(IP) routing prefixes under the control of one or more network operators on\nbehalf of a single administrative entity or domain that presents a common, clearly\ndefined routing policy to the internet.', - name: 'as', - type: 'group', - fields: { - 'as.number': { - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - name: 'number', - type: 'long', - }, - 'as.organization.name': { - description: 'Organization name.', - example: 'Google LLC', - name: 'organization.name', - type: 'keyword', - }, - }, - }, - }); - }); - - test('Filebeat transformation', async () => { - const convertData: Schema = cloneDeep(filebeatSchema).slice(0, 1); - convertData[0].fields = isArray(convertData[0].fields) - ? convertData[0].fields!.slice(0, 6) - : []; - - expect(convertSchemaToAssociativeArray(convertData)).toEqual({ - '@timestamp': { - description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', - example: '2016-05-23T08:05:34.853Z', - name: '@timestamp', - type: 'date', - }, - labels: { - description: - 'Custom key/value pairs.\n\nCan be used to add meta information to events. Should not contain nested objects.\nAll values are stored as keyword.\n\nExample: `docker` and `k8s` labels.', - example: '{"application": "foo-bar", "env": "production"}', - name: 'labels', - type: 'object', - }, - message: { - description: - 'For log events the message field contains the log message, optimized\nfor viewing in a log viewer.\n\nFor structured logs without an original message field, other fields can be concatenated\nto form a human-readable summary of the event.\n\nIf multiple messages exist, they can be combined into one message.', - example: 'Hello World', - name: 'message', - type: 'text', - }, - tags: { - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - name: 'tags', - type: 'keyword', - }, - agent: { - description: - 'The agent fields contain the data about the software entity, if\nany, that collects, detects, or observes events on a host, or takes measurements\non a host.\n\nExamples include Beats. Agents may also run on observers. ECS agent.* fields\nshall be populated with details of the agent running on the host or observer\nwhere the event happened or the measurement was taken.', - name: 'agent', - type: 'group', - fields: { - 'agent.ephemeral_id': { - description: - 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - name: 'ephemeral_id', - type: 'keyword', - }, - 'agent.id': { - description: - 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', - example: '8a4f500d', - name: 'id', - type: 'keyword', - }, - 'agent.name': { - description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', - example: 'foo', - name: 'name', - type: 'keyword', - }, - 'agent.type': { - description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', - example: 'filebeat', - name: 'type', - type: 'keyword', - }, - 'agent.version': { - description: 'Version of the agent.', - example: '6.0.0-rc2', - name: 'version', - type: 'keyword', - }, - }, - }, - as: { - description: - 'An autonomous system (AS) is a collection of connected Internet Protocol\n(IP) routing prefixes under the control of one or more network operators on\nbehalf of a single administrative entity or domain that presents a common, clearly\ndefined routing policy to the internet.', - name: 'as', - type: 'group', - fields: { - 'as.number': { - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - name: 'number', - type: 'long', - }, - 'as.organization.name': { - description: 'Organization name.', - example: 'Google LLC', - name: 'organization.name', - type: 'keyword', - }, - }, - }, - }); - }); - - test('Packetbeat transformation', async () => { - const convertData: Schema = cloneDeep(packetbeatSchema).slice(0, 1); - convertData[0].fields = isArray(convertData[0].fields) - ? convertData[0].fields!.slice(0, 6) - : []; - - expect(convertSchemaToAssociativeArray(convertData)).toEqual({ - '@timestamp': { - description: - 'Date/time when the event originated.\n\nThis is the date/time extracted from the event, typically representing when\nthe event was generated by the source.\n\nIf the event source has no original timestamp, this value is typically populated\nby the first time the event was received by the pipeline.\n\nRequired field for all events.', - example: '2016-05-23T08:05:34.853Z', - name: '@timestamp', - type: 'date', - }, - labels: { - description: - 'Custom key/value pairs.\n\nCan be used to add meta information to events. Should not contain nested objects.\nAll values are stored as keyword.\n\nExample: `docker` and `k8s` labels.', - example: '{"application": "foo-bar", "env": "production"}', - name: 'labels', - type: 'object', - }, - message: { - description: - 'For log events the message field contains the log message, optimized\nfor viewing in a log viewer.\n\nFor structured logs without an original message field, other fields can be concatenated\nto form a human-readable summary of the event.\n\nIf multiple messages exist, they can be combined into one message.', - example: 'Hello World', - name: 'message', - type: 'text', - }, - tags: { - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - name: 'tags', - type: 'keyword', - }, - agent: { - description: - 'The agent fields contain the data about the software entity, if\nany, that collects, detects, or observes events on a host, or takes measurements\non a host.\n\nExamples include Beats. Agents may also run on observers. ECS agent.* fields\nshall be populated with details of the agent running on the host or observer\nwhere the event happened or the measurement was taken.', - name: 'agent', - type: 'group', - fields: { - 'agent.ephemeral_id': { - description: - 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - name: 'ephemeral_id', - type: 'keyword', - }, - 'agent.id': { - description: - 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', - example: '8a4f500d', - name: 'id', - type: 'keyword', - }, - 'agent.name': { - description: - 'Custom name of the agent.\n\nThis is a name that can be given to an agent. This can be helpful if for example\ntwo Filebeat instances are running on the same host but a human readable separation\nis needed on which Filebeat instance data is coming from.\n\nIf no name is given, the name is often left empty.', - example: 'foo', - name: 'name', - type: 'keyword', - }, - 'agent.type': { - description: - 'Type of the agent.\n\nThe agent type stays always the same and should be given by the agent used.\nIn case of Filebeat the agent would always be Filebeat also if two Filebeat\ninstances are run on the same machine.', - example: 'filebeat', - name: 'type', - type: 'keyword', - }, - 'agent.version': { - description: 'Version of the agent.', - example: '6.0.0-rc2', - name: 'version', - type: 'keyword', - }, - }, - }, - as: { - description: - 'An autonomous system (AS) is a collection of connected Internet Protocol\n(IP) routing prefixes under the control of one or more network operators on\nbehalf of a single administrative entity or domain that presents a common, clearly\ndefined routing policy to the internet.', - name: 'as', - type: 'group', - fields: { - 'as.number': { - description: - 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', - example: 15169, - name: 'number', - type: 'long', - }, - 'as.organization.name': { - description: 'Organization name.', - example: 'Google LLC', - name: 'organization.name', - type: 'keyword', - }, - }, - }, - }); - }); - }); - - describe('GetIndexSchemaDoc', () => { - test('Filebeat transformation', async () => { - expect(Object.keys(getIndexSchemaDoc('auditbeat'))).toEqual([ - '_id', - '_index', - '@timestamp', - 'labels', - 'message', - 'tags', - 'agent', - 'as', - 'client', - 'cloud', - 'code_signature', - 'container', - 'destination', - 'dll', - 'dns', - 'ecs', - 'error', - 'event', - 'file', - 'geo', - 'group', - 'hash', - 'host', - 'http', - 'interface', - 'log', - 'network', - 'observer', - 'organization', - 'os', - 'package', - 'pe', - 'process', - 'registry', - 'related', - 'rule', - 'server', - 'service', - 'source', - 'threat', - 'tls', - 'tracing', - 'url', - 'user', - 'user_agent', - 'vlan', - 'vulnerability', - 'agent.hostname', - 'beat.timezone', - 'fields', - 'beat.name', - 'beat.hostname', - 'timeseries.instance', - 'cloud.project.id', - 'cloud.image.id', - 'meta.cloud.provider', - 'meta.cloud.instance_id', - 'meta.cloud.instance_name', - 'meta.cloud.machine_type', - 'meta.cloud.availability_zone', - 'meta.cloud.project_id', - 'meta.cloud.region', - 'docker', - 'kubernetes', - 'jolokia.agent.version', - 'jolokia.agent.id', - 'jolokia.server.product', - 'jolokia.server.version', - 'jolokia.server.vendor', - 'jolokia.url', - 'jolokia.secured', - 'auditd', - 'geoip', - 'socket', - 'system.audit', - ]); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/utils/beat_schema/index.ts b/x-pack/plugins/security_solution/server/utils/beat_schema/index.ts deleted file mode 100644 index 58627a199a1814..00000000000000 --- a/x-pack/plugins/security_solution/server/utils/beat_schema/index.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { get, has, isArray, isEmpty, isNumber, isString, memoize, pick } from 'lodash/fp'; - -import { - auditbeatSchema, - baseCategoryFields, - ecsSchema, - extraSchemaField, - filebeatSchema, - packetbeatSchema, - winlogbeatSchema, -} from './8.0.0'; -import { OutputSchema, Schema, SchemaFields, SchemaItem } from './type'; - -export * from './type'; -export { baseCategoryFields }; -export const convertSchemaToAssociativeArray = (schema: Schema): OutputSchema => - schema.reduce((accumulator: OutputSchema, item: Partial) => { - if (item.fields != null && !isEmpty(item.fields)) { - return { - ...accumulator, - ...convertFieldsToAssociativeArray(item), - }; - } - return accumulator; - }, {}); - -const paramsToPick = ['description', 'example', 'name', 'type', 'format']; - -const onlyStringOrNumber = (fields: object) => - Object.keys(fields).reduce((acc, item) => { - const value = get(item, fields); - return { - ...acc, - [item]: isString(value) || isNumber(value) ? value : JSON.stringify(value), - }; - }, {}); - -const convertFieldsToAssociativeArray = ( - schemaFields: Partial, - path: string = '' -): OutputSchema => - schemaFields.fields && isArray(schemaFields.fields) - ? schemaFields.fields.reduce((accumulator: OutputSchema, item: Partial) => { - if (item.name) { - const attr = isEmpty(path) ? item.name : `${path}.${item.name}`; - if (!isEmpty(item.fields) && isEmpty(path)) { - return { - ...accumulator, - [attr]: { - ...onlyStringOrNumber(pick(paramsToPick, item)), - fields: { - ...convertFieldsToAssociativeArray(item, attr), - }, - }, - }; - } else if (!isEmpty(item.fields) && !isEmpty(path)) { - return { - ...accumulator, - [attr]: onlyStringOrNumber(pick(paramsToPick, item)), - ...convertFieldsToAssociativeArray(item, attr), - }; - } else { - return { - ...accumulator, - [attr]: onlyStringOrNumber(pick(paramsToPick, item)), - }; - } - } - return accumulator; - }, {}) - : {}; - -export const getIndexSchemaDoc = memoize((index: string) => { - if (index.match('auditbeat') != null) { - return { - ...extraSchemaField, - ...convertSchemaToAssociativeArray(auditbeatSchema), - }; - } else if (index.match('filebeat') != null) { - return { - ...extraSchemaField, - ...convertSchemaToAssociativeArray(filebeatSchema), - }; - } else if (index.match('packetbeat') != null) { - return { - ...extraSchemaField, - ...convertSchemaToAssociativeArray(packetbeatSchema), - }; - } else if (index.match('winlogbeat') != null) { - return { - ...extraSchemaField, - ...convertSchemaToAssociativeArray(winlogbeatSchema), - }; - } - return { - ...extraSchemaField, - ...convertSchemaToAssociativeArray(ecsSchema), - }; -}); - -export const hasDocumentation = (index: string, path: string): boolean => { - const splitPath = path.split('.'); - const category = splitPath.length > 0 ? splitPath[0] : null; - if (category === null) { - return false; - } - if (splitPath.length > 1) { - return has([category, 'fields', path], getIndexSchemaDoc(index)); - } - return has(category, getIndexSchemaDoc(index)); -}; - -export const getDocumentation = (index: string, path: string) => { - const splitPath = path.split('.'); - const category = splitPath.length > 0 ? splitPath[0] : null; - if (category === null) { - return {}; - } - if (splitPath.length > 1) { - return get([category, 'fields', path], getIndexSchemaDoc(index)) || {}; - } - return get(category, getIndexSchemaDoc(index)) || {}; -}; diff --git a/x-pack/plugins/security_solution/server/utils/beat_schema/type.ts b/x-pack/plugins/security_solution/server/utils/beat_schema/type.ts deleted file mode 100644 index 722589ce7e2bb4..00000000000000 --- a/x-pack/plugins/security_solution/server/utils/beat_schema/type.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/* - * BEAT Interface - * - */ - -export interface SchemaFields { - default_field: boolean; - default_fields: boolean; - definition: string; - deprecated: string; - description: string; - doc_values: boolean; - example: string | number | object | boolean; - footnote: string; - format: string; - group: number; - index: boolean; - ignore_above: number; - input_format: string; - level: string; - migration: boolean; - multi_fields: object[]; - name: string; - norms: boolean; - object_type: string; - object_type_mapping_type: string; - output_format: string; - output_precision: number; - overwrite: boolean; - path: string; - possible_values: string[] | number[]; - release: string; - required: boolean; - reusable: object; - short: string; - title: string; - type: string; - fields: Array>; -} - -export interface SchemaItem { - anchor: string; - key: string; - title: string; - description: string; - short_config: boolean; - release: string; - fields: Array>; -} - -export type Schema = Array>; - -/* - * Associative Array Output Interface - * - */ - -export interface RequiredSchemaField { - description: string; - example: string | number; - name: string; - type: string; - format: string; - fields: Readonly>>; -} - -export type OutputSchema = Readonly>>; diff --git a/x-pack/test/api_integration/apis/security_solution/sources.ts b/x-pack/test/api_integration/apis/security_solution/sources.ts index f99dd4c65fc83e..1ec4bfda8492de 100644 --- a/x-pack/test/api_integration/apis/security_solution/sources.ts +++ b/x-pack/test/api_integration/apis/security_solution/sources.ts @@ -5,110 +5,107 @@ */ import expect from '@kbn/expect'; -import { sourceQuery } from '../../../../plugins/security_solution/public/common/containers/source/index.gql_query'; -import { SourceQuery } from '../../../../plugins/security_solution/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); describe('sources', () => { before(() => esArchiver.load('auditbeat/default')); after(() => esArchiver.unload('auditbeat/default')); it('Make sure that we get source information when auditbeat indices is there', async () => { - const resp = await client.query({ - query: sourceQuery, - variables: { - sourceId: 'default', - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - }, - }); - const sourceStatus = resp.data.source.status; - // test data in x-pack/test/functional/es_archives/auditbeat_test_data/data.json.gz - expect(sourceStatus.indexFields.length).to.be(397); - expect(sourceStatus.indicesExist).to.be(true); + const { body: sourceStatus } = await supertest + .post('/internal/search/securitySolutionIndexFields/') + .set('kbn-xsrf', 'true') + .send({ + indices: ['auditbeat-*'], + onlyCheckIfIndicesExist: false, + }) + .expect(200); + + expect(sourceStatus.indexFields.length).to.be(351); + expect(sourceStatus.indicesExist).to.eql(['auditbeat-*']); }); it('should find indexes as being available when they exist', async () => { - const resp = await client.query({ - query: sourceQuery, - variables: { - sourceId: 'default', - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - }, - }); - const sourceStatus = resp.data.source.status; - expect(sourceStatus.indicesExist).to.be(true); + const { body: sourceStatus } = await supertest + .post('/internal/search/securitySolutionIndexFields/') + .set('kbn-xsrf', 'true') + .send({ + indices: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + onlyCheckIfIndicesExist: false, + }) + .expect(200); + + expect(sourceStatus.indicesExist).to.eql(['auditbeat-*', 'winlogbeat-*']); }); it('should not find indexes as existing when there is an empty array of them', async () => { - const resp = await client.query({ - query: sourceQuery, - variables: { - sourceId: 'default', - defaultIndex: [], - docValueFields: [], - }, - }); - const sourceStatus = resp.data.source.status; - expect(sourceStatus.indicesExist).to.be(false); + const { body: sourceStatus } = await supertest + .post('/internal/search/securitySolutionIndexFields/') + .set('kbn-xsrf', 'true') + .send({ + indices: [], + onlyCheckIfIndicesExist: false, + }) + .expect(200); + + expect(sourceStatus.indicesExist).to.eql([]); }); it('should not find indexes as existing when there is a _all within it', async () => { - const resp = await client.query({ - query: sourceQuery, - variables: { - sourceId: 'default', - defaultIndex: ['_all'], - docValueFields: [], - }, - }); - const sourceStatus = resp.data.source.status; - expect(sourceStatus.indicesExist).to.be(false); + const { body: sourceStatus } = await supertest + .post('/internal/search/securitySolutionIndexFields/') + .set('kbn-xsrf', 'true') + .send({ + indices: ['_all'], + onlyCheckIfIndicesExist: false, + }) + .expect(200); + + expect(sourceStatus.indicesExist).to.eql([]); }); it('should not find indexes as existing when there are empty strings within it', async () => { - const resp = await client.query({ - query: sourceQuery, - variables: { - sourceId: 'default', - defaultIndex: [''], - docValueFields: [], - }, - }); - const sourceStatus = resp.data.source.status; - expect(sourceStatus.indicesExist).to.be(false); + const { body: sourceStatus } = await supertest + .post('/internal/search/securitySolutionIndexFields/') + .set('kbn-xsrf', 'true') + .send({ + indices: [''], + onlyCheckIfIndicesExist: false, + }) + .expect(200); + + expect(sourceStatus.indicesExist).to.eql([]); }); it('should not find indexes as existing when there are blank spaces within it', async () => { - const resp = await client.query({ - query: sourceQuery, - variables: { - sourceId: 'default', - defaultIndex: [' '], - docValueFields: [], - }, - }); - const sourceStatus = resp.data.source.status; - expect(sourceStatus.indicesExist).to.be(false); + const { body: sourceStatus } = await supertest + .post('/internal/search/securitySolutionIndexFields/') + .set('kbn-xsrf', 'true') + .send({ + indices: [' '], + onlyCheckIfIndicesExist: false, + }) + .expect(200); + + expect(sourceStatus.indicesExist).to.eql([]); }); it('should find indexes when one is an empty index but the others are valid', async () => { - const resp = await client.query({ - query: sourceQuery, - variables: { - sourceId: 'default', - defaultIndex: ['', 'auditbeat-*'], - docValueFields: [], - }, - }); - const sourceStatus = resp.data.source.status; - expect(sourceStatus.indicesExist).to.be(true); + const { body: sourceStatus } = await supertest + .post('/internal/search/securitySolutionIndexFields/') + .set('kbn-xsrf', 'true') + .send({ + indices: ['', 'auditbeat-*'], + onlyCheckIfIndicesExist: false, + }) + .expect(200); + + expect(sourceStatus.indicesExist).to.eql(['auditbeat-*']); }); }); } From 27c32edd232caa325d345ff5a1cc8479f4a41be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Wed, 23 Sep 2020 22:21:34 +0200 Subject: [PATCH 19/35] [Security Solution] Cleanup Overview graphql (#78272) * [Security Solution] Cleanup Tls graphql * [Security Solution] Cleanup Uncommon Processes graphql * [Security Solution] Cleanup Overview graphql --- .../security_solution/hosts/overview/index.ts | 2 +- .../security_solution/index.ts | 4 +- .../public/graphql/introspection.json | 336 --------------- .../security_solution/public/graphql/types.ts | 206 --------- .../overview_host_stats/index.test.tsx | 6 +- .../components/overview_host_stats/index.tsx | 8 +- .../components/overview_host_stats/mock.ts | 38 +- .../overview_network_stats/index.test.tsx | 8 +- .../overview_network_stats/index.tsx | 8 +- .../components/overview_network_stats/mock.ts | 24 +- .../overview_host/index.gql_query.ts | 43 -- .../containers/overview_host/index.tsx | 6 +- .../overview_network/index.gql_query.ts | 40 -- .../security_solution/server/graphql/index.ts | 2 - .../server/graphql/overview/index.ts | 8 - .../server/graphql/overview/resolvers.ts | 45 -- .../server/graphql/overview/schema.gql.ts | 57 --- .../security_solution/server/graphql/types.ts | 319 -------------- .../security_solution/server/init_server.ts | 2 - .../server/lib/compose/kibana.ts | 3 - .../lib/overview/elastic_adapter.test.ts | 187 --------- .../lib/overview/elasticsearch_adapter.ts | 132 ------ .../server/lib/overview/index.ts | 28 -- .../server/lib/overview/mock.ts | 176 -------- .../server/lib/overview/query.dsl.ts | 397 ------------------ .../server/lib/overview/types.ts | 109 ----- .../security_solution/server/lib/types.ts | 2 - .../factory/hosts/overview/index.ts | 4 +- .../apis/security_solution/index.js | 4 +- .../apis/security_solution/overview_host.ts | 2 + .../security_solution/overview_network.ts | 2 + 31 files changed, 59 insertions(+), 2149 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_host/index.gql_query.ts delete mode 100644 x-pack/plugins/security_solution/public/overview/containers/overview_network/index.gql_query.ts delete mode 100644 x-pack/plugins/security_solution/server/graphql/overview/index.ts delete mode 100644 x-pack/plugins/security_solution/server/graphql/overview/resolvers.ts delete mode 100644 x-pack/plugins/security_solution/server/graphql/overview/schema.gql.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/overview/elastic_adapter.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/overview/elasticsearch_adapter.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/overview/index.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/overview/mock.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/overview/query.dsl.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/overview/types.ts diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/overview/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/overview/index.ts index 569ed611bd35bc..4416cbb023f106 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/overview/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/overview/index.ts @@ -10,7 +10,7 @@ import { RequestBasicOptions } from '../..'; export type HostOverviewRequestOptions = RequestBasicOptions; -export interface HostOverviewStrategyResponse extends IEsSearchResponse { +export interface HostsOverviewStrategyResponse extends IEsSearchResponse { inspect?: Maybe; overviewHost: { auditbeatAuditd?: Maybe; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts index af9faef89af466..39443e596273a8 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts @@ -9,7 +9,7 @@ import { ESQuery } from '../../typed_json'; import { HostDetailsStrategyResponse, HostDetailsRequestOptions, - HostOverviewStrategyResponse, + HostsOverviewStrategyResponse, HostAuthenticationsRequestOptions, HostAuthenticationsStrategyResponse, HostOverviewRequestOptions, @@ -107,7 +107,7 @@ export type StrategyResponseType = T extends HostsQ : T extends HostsQueries.details ? HostDetailsStrategyResponse : T extends HostsQueries.overview - ? HostOverviewStrategyResponse + ? HostsOverviewStrategyResponse : T extends HostsQueries.authentications ? HostAuthenticationsStrategyResponse : T extends HostsQueries.firstLastSeen diff --git a/x-pack/plugins/security_solution/public/graphql/introspection.json b/x-pack/plugins/security_solution/public/graphql/introspection.json index 568a960f0804ed..2f312c461ff8cc 100644 --- a/x-pack/plugins/security_solution/public/graphql/introspection.json +++ b/x-pack/plugins/security_solution/public/graphql/introspection.json @@ -2088,104 +2088,6 @@ "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 - }, - { - "name": "defaultIndex", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - } - }, - "defaultValue": null - } - ], - "type": { "kind": "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 - }, - { - "name": "defaultIndex", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - } - }, - "defaultValue": null - } - ], - "type": { "kind": "OBJECT", "name": "OverviewHostData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "whoAmI", "description": "Just a simple example to get the app name", @@ -8901,244 +8803,6 @@ "enumValues": null, "possibleTypes": null }, - { - "kind": "OBJECT", - "name": "OverviewNetworkData", - "description": "", - "fields": [ - { - "name": "auditbeatSocket", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "filebeatCisco", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "filebeatNetflow", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "filebeatPanw", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "filebeatSuricata", - "description": "", - "args": [], - "type": { "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": "packetbeatDNS", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "packetbeatFlow", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "packetbeatTLS", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "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": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "auditbeatFIM", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "auditbeatLogin", - "description": "", - "args": [], - "type": { "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 - }, - { - "name": "endgameDns", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "endgameFile", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "endgameImageLoad", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "endgameNetwork", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "endgameProcess", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "endgameRegistry", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "endgameSecurity", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "filebeatSystemModule", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "winlogbeatSecurity", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "winlogbeatMWSysmonOperational", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, { "kind": "OBJECT", "name": "SayMyName", diff --git a/x-pack/plugins/security_solution/public/graphql/types.ts b/x-pack/plugins/security_solution/public/graphql/types.ts index 0bce952912c5c0..bcb580a1a2988e 100644 --- a/x-pack/plugins/security_solution/public/graphql/types.ts +++ b/x-pack/plugins/security_solution/public/graphql/types.ts @@ -556,10 +556,6 @@ export interface Source { NetworkDnsHistogram: NetworkDsOverTimeData; NetworkHttp: NetworkHttpData; - - OverviewNetwork?: Maybe; - - OverviewHost?: Maybe; /** Just a simple example to get the app name */ whoAmI?: Maybe; } @@ -1832,64 +1828,6 @@ export interface NetworkHttpItem { statuses: string[]; } -export interface OverviewNetworkData { - auditbeatSocket?: Maybe; - - filebeatCisco?: Maybe; - - filebeatNetflow?: Maybe; - - filebeatPanw?: Maybe; - - filebeatSuricata?: Maybe; - - filebeatZeek?: Maybe; - - packetbeatDNS?: Maybe; - - packetbeatFlow?: Maybe; - - packetbeatTLS?: Maybe; - - inspect?: Maybe; -} - -export interface OverviewHostData { - auditbeatAuditd?: Maybe; - - auditbeatFIM?: Maybe; - - auditbeatLogin?: Maybe; - - auditbeatPackage?: Maybe; - - auditbeatProcess?: Maybe; - - auditbeatUser?: Maybe; - - endgameDns?: Maybe; - - endgameFile?: Maybe; - - endgameImageLoad?: Maybe; - - endgameNetwork?: Maybe; - - endgameProcess?: Maybe; - - endgameRegistry?: Maybe; - - endgameSecurity?: Maybe; - - filebeatSystemModule?: Maybe; - - winlogbeatSecurity?: Maybe; - - winlogbeatMWSysmonOperational?: Maybe; - - inspect?: Maybe; -} - export interface SayMyName { /** The id of the source */ appName: string; @@ -2487,24 +2425,6 @@ export interface NetworkHttpSourceArgs { defaultIndex: string[]; } -export interface OverviewNetworkSourceArgs { - id?: Maybe; - - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; -} -export interface OverviewHostSourceArgs { - id?: Maybe; - - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; -} export interface IndicesExistSourceStatusArgs { defaultIndex: string[]; } @@ -3957,132 +3877,6 @@ export namespace GetUsersQuery { }; } -export namespace GetOverviewHostQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - OverviewHost: Maybe; - }; - - export type OverviewHost = { - __typename?: 'OverviewHostData'; - - auditbeatAuditd: Maybe; - - auditbeatFIM: Maybe; - - auditbeatLogin: Maybe; - - auditbeatPackage: Maybe; - - auditbeatProcess: Maybe; - - auditbeatUser: Maybe; - - endgameDns: Maybe; - - endgameFile: Maybe; - - endgameImageLoad: Maybe; - - endgameNetwork: Maybe; - - endgameProcess: Maybe; - - endgameRegistry: Maybe; - - endgameSecurity: Maybe; - - filebeatSystemModule: Maybe; - - winlogbeatSecurity: Maybe; - - winlogbeatMWSysmonOperational: Maybe; - - inspect: Maybe; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetOverviewNetworkQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - OverviewNetwork: Maybe; - }; - - export type OverviewNetwork = { - __typename?: 'OverviewNetworkData'; - - auditbeatSocket: Maybe; - - filebeatCisco: Maybe; - - filebeatNetflow: Maybe; - - filebeatPanw: Maybe; - - filebeatSuricata: Maybe; - - filebeatZeek: Maybe; - - packetbeatDNS: Maybe; - - packetbeatFlow: Maybe; - - packetbeatTLS: Maybe; - - inspect: Maybe; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - export namespace GetAllTimeline { export type Variables = { pageInfo: PageInfoTimeline; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host_stats/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_host_stats/index.test.tsx index 75295d9e45c0cc..3a12f0c038b8c4 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host_stats/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host_stats/index.test.tsx @@ -14,7 +14,7 @@ import { TestProviders } from '../../../common/mock/test_providers'; describe('Overview Host Stat Data', () => { describe('rendering', () => { test('it renders the default OverviewHostStats', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); }); @@ -22,7 +22,7 @@ describe('Overview Host Stat Data', () => { test('it does NOT show loading indicator when loading is false', () => { const wrapper = mount( - + ); @@ -42,7 +42,7 @@ describe('Overview Host Stat Data', () => { test('it shows loading indicator when loading is true', () => { const wrapper = mount( - + ); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host_stats/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_host_stats/index.tsx index 92250ed3c549b7..ef595476d8a941 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host_stats/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host_stats/index.tsx @@ -9,16 +9,18 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; import styled from 'styled-components'; -import { OverviewHostData } from '../../../graphql/types'; +import { HostsOverviewStrategyResponse } from '../../../../common/search_strategy'; import { FormattedStat, StatGroup } from '../types'; import { StatValue } from '../stat_value'; interface OverviewHostProps { - data: OverviewHostData; + data: HostsOverviewStrategyResponse['overviewHost']; loading: boolean; } -export const getOverviewHostStats = (data: OverviewHostData): FormattedStat[] => [ +export const getOverviewHostStats = ( + data: HostsOverviewStrategyResponse['overviewHost'] +): FormattedStat[] => [ { count: data.auditbeatAuditd ?? 0, title: ( diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host_stats/mock.ts b/x-pack/plugins/security_solution/public/overview/components/overview_host_stats/mock.ts index 63b3a484c1eaa9..986d02faac37a1 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host_stats/mock.ts +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host_stats/mock.ts @@ -4,25 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import { OverviewHostData } from '../../../graphql/types'; +import { HostsOverviewStrategyResponse } from '../../../../common/search_strategy'; -export const mockData: { OverviewHost: OverviewHostData } = { - OverviewHost: { - auditbeatAuditd: 73847, - auditbeatFIM: 107307, - auditbeatLogin: 60015, - auditbeatPackage: 2003, - auditbeatProcess: 1200, - auditbeatUser: 1979, - endgameDns: 39123, - endgameFile: 39456, - endgameImageLoad: 39789, - endgameNetwork: 39101112, - endgameProcess: 39131415, - endgameRegistry: 39161718, - endgameSecurity: 39202122, - filebeatSystemModule: 568, - winlogbeatSecurity: 195929, - winlogbeatMWSysmonOperational: 101070, - }, +export const mockData: HostsOverviewStrategyResponse['overviewHost'] = { + auditbeatAuditd: 73847, + auditbeatFIM: 107307, + auditbeatLogin: 60015, + auditbeatPackage: 2003, + auditbeatProcess: 1200, + auditbeatUser: 1979, + endgameDns: 39123, + endgameFile: 39456, + endgameImageLoad: 39789, + endgameNetwork: 39101112, + endgameProcess: 39131415, + endgameRegistry: 39161718, + endgameSecurity: 39202122, + filebeatSystemModule: 568, + winlogbeatSecurity: 195929, + winlogbeatMWSysmonOperational: 101070, }; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_network_stats/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_network_stats/index.test.tsx index 0add7c1a020472..2f801ae1f3623f 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_network_stats/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_network_stats/index.test.tsx @@ -14,9 +14,7 @@ import { TestProviders } from '../../../common/mock/test_providers'; describe('Overview Network Stat Data', () => { describe('rendering', () => { test('it renders the default OverviewNetworkStats', () => { - const wrapper = shallow( - - ); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); }); @@ -24,7 +22,7 @@ describe('Overview Network Stat Data', () => { test('it does NOT show loading indicator when loading is false', () => { const wrapper = mount( - + ); @@ -45,7 +43,7 @@ describe('Overview Network Stat Data', () => { test('it shows the loading indicator when loading is true', () => { const wrapper = mount( - + ); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_network_stats/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_network_stats/index.tsx index d3e16af7115ac1..c6ad56b7243d42 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_network_stats/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_network_stats/index.tsx @@ -9,16 +9,18 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; import styled from 'styled-components'; -import { OverviewNetworkData } from '../../../graphql/types'; +import { NetworkOverviewStrategyResponse } from '../../../../common/search_strategy'; import { FormattedStat, StatGroup } from '../types'; import { StatValue } from '../stat_value'; interface OverviewNetworkProps { - data: OverviewNetworkData; + data: NetworkOverviewStrategyResponse['overviewNetwork']; loading: boolean; } -export const getOverviewNetworkStats = (data: OverviewNetworkData): FormattedStat[] => [ +export const getOverviewNetworkStats = ( + data: NetworkOverviewStrategyResponse['overviewNetwork'] +): FormattedStat[] => [ { count: data.auditbeatSocket ?? 0, title: ( diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_network_stats/mock.ts b/x-pack/plugins/security_solution/public/overview/components/overview_network_stats/mock.ts index f55d6a1577ccd8..1eb337f1ea4549 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_network_stats/mock.ts +++ b/x-pack/plugins/security_solution/public/overview/components/overview_network_stats/mock.ts @@ -4,18 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { OverviewNetworkData } from '../../../graphql/types'; +import { NetworkOverviewStrategyResponse } from '../../../../common/search_strategy'; -export const mockData: { OverviewNetwork: OverviewNetworkData } = { - OverviewNetwork: { - auditbeatSocket: 12, - filebeatCisco: 999, - filebeatNetflow: 7777, - filebeatPanw: 66, - filebeatSuricata: 60015, - filebeatZeek: 2003, - packetbeatDNS: 10277307, - packetbeatFlow: 16, - packetbeatTLS: 3400000, - }, +export const mockData: NetworkOverviewStrategyResponse['overviewNetwork'] = { + auditbeatSocket: 12, + filebeatCisco: 999, + filebeatNetflow: 7777, + filebeatPanw: 66, + filebeatSuricata: 60015, + filebeatZeek: 2003, + packetbeatDNS: 10277307, + packetbeatFlow: 16, + packetbeatTLS: 3400000, }; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.gql_query.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.gql_query.ts deleted file mode 100644 index 6f17bf6915aa45..00000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.gql_query.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -export const overviewHostQuery = gql` - query GetOverviewHostQuery( - $sourceId: ID! - $timerange: TimerangeInput! - $filterQuery: String - $defaultIndex: [String!]! - $inspect: Boolean! - ) { - source(id: $sourceId) { - id - OverviewHost(timerange: $timerange, filterQuery: $filterQuery, defaultIndex: $defaultIndex) { - auditbeatAuditd - auditbeatFIM - auditbeatLogin - auditbeatPackage - auditbeatProcess - auditbeatUser - endgameDns - endgameFile - endgameImageLoad - endgameNetwork - endgameProcess - endgameRegistry - endgameSecurity - filebeatSystemModule - winlogbeatSecurity - winlogbeatMWSysmonOperational - inspect @include(if: $inspect) { - dsl - response - } - } - } - } -`; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.tsx b/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.tsx index ac439107cb4a5c..946cd33088a45a 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_host/index.tsx @@ -11,7 +11,7 @@ import deepEqual from 'fast-deep-equal'; import { HostsQueries, HostOverviewRequestOptions, - HostOverviewStrategyResponse, + HostsOverviewStrategyResponse, } from '../../../../common/search_strategy/security_solution'; import { useKibana } from '../../../common/lib/kibana'; import { inputsModel } from '../../../common/store/inputs'; @@ -32,7 +32,7 @@ export interface HostOverviewArgs { id: string; inspect: InspectResponse; isInspected: boolean; - overviewHost: HostOverviewStrategyResponse['overviewHost']; + overviewHost: HostsOverviewStrategyResponse['overviewHost']; refetch: inputsModel.Refetch; } @@ -85,7 +85,7 @@ export const useHostOverview = ({ setLoading(true); const searchSubscription$ = data.search - .search(request, { + .search(request, { strategy: 'securitySolutionSearchStrategy', abortSignal: abortCtrl.current.signal, }) diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.gql_query.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.gql_query.ts deleted file mode 100644 index d40ab900b91a76..00000000000000 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.gql_query.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -export const overviewNetworkQuery = gql` - query GetOverviewNetworkQuery( - $sourceId: ID! - $timerange: TimerangeInput! - $filterQuery: String - $defaultIndex: [String!]! - $inspect: Boolean! - ) { - source(id: $sourceId) { - id - OverviewNetwork( - timerange: $timerange - filterQuery: $filterQuery - defaultIndex: $defaultIndex - ) { - auditbeatSocket - filebeatCisco - filebeatNetflow - filebeatPanw - filebeatSuricata - filebeatZeek - packetbeatDNS - packetbeatFlow - packetbeatTLS - inspect @include(if: $inspect) { - dsl - response - } - } - } - } -`; diff --git a/x-pack/plugins/security_solution/server/graphql/index.ts b/x-pack/plugins/security_solution/server/graphql/index.ts index e949150c47c6cf..2de6ef32b57031 100644 --- a/x-pack/plugins/security_solution/server/graphql/index.ts +++ b/x-pack/plugins/security_solution/server/graphql/index.ts @@ -15,7 +15,6 @@ import { ipDetailsSchemas } from './ip_details'; import { kpiHostsSchema } from './kpi_hosts'; import { kpiNetworkSchema } from './kpi_network'; import { networkSchema } from './network'; -import { overviewSchema } from './overview'; import { dateSchema } from './scalar_date'; import { noteSchema } from './note'; import { pinnedEventSchema } from './pinned_event'; @@ -44,7 +43,6 @@ export const schemas = [ matrixHistogramSchema, networkSchema, noteSchema, - overviewSchema, pinnedEventSchema, rootSchema, sourcesSchema, diff --git a/x-pack/plugins/security_solution/server/graphql/overview/index.ts b/x-pack/plugins/security_solution/server/graphql/overview/index.ts deleted file mode 100644 index 58cf182ccd9766..00000000000000 --- a/x-pack/plugins/security_solution/server/graphql/overview/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { createOverviewResolvers } from './resolvers'; -export { overviewSchema } from './schema.gql'; diff --git a/x-pack/plugins/security_solution/server/graphql/overview/resolvers.ts b/x-pack/plugins/security_solution/server/graphql/overview/resolvers.ts deleted file mode 100644 index a7bafabb640928..00000000000000 --- a/x-pack/plugins/security_solution/server/graphql/overview/resolvers.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { 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, - QuerySourceResolver ->; - -export type QueryOverviewHostResolver = ChildResolverOf< - AppResolverOf, - 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); - }, - }, -}); diff --git a/x-pack/plugins/security_solution/server/graphql/overview/schema.gql.ts b/x-pack/plugins/security_solution/server/graphql/overview/schema.gql.ts deleted file mode 100644 index 7ab4f9fdb18d6c..00000000000000 --- a/x-pack/plugins/security_solution/server/graphql/overview/schema.gql.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -export const overviewSchema = gql` - type OverviewNetworkData { - auditbeatSocket: Float - filebeatCisco: Float - filebeatNetflow: Float - filebeatPanw: Float - filebeatSuricata: Float - filebeatZeek: Float - packetbeatDNS: Float - packetbeatFlow: Float - packetbeatTLS: Float - inspect: Inspect - } - - type OverviewHostData { - auditbeatAuditd: Float - auditbeatFIM: Float - auditbeatLogin: Float - auditbeatPackage: Float - auditbeatProcess: Float - auditbeatUser: Float - endgameDns: Float - endgameFile: Float - endgameImageLoad: Float - endgameNetwork: Float - endgameProcess: Float - endgameRegistry: Float - endgameSecurity: Float - filebeatSystemModule: Float - winlogbeatSecurity: Float - winlogbeatMWSysmonOperational: Float - inspect: Inspect - } - - extend type Source { - OverviewNetwork( - id: String - timerange: TimerangeInput! - filterQuery: String - defaultIndex: [String!]! - ): OverviewNetworkData - OverviewHost( - id: String - timerange: TimerangeInput! - filterQuery: String - defaultIndex: [String!]! - ): OverviewHostData - } -`; diff --git a/x-pack/plugins/security_solution/server/graphql/types.ts b/x-pack/plugins/security_solution/server/graphql/types.ts index 4c85c08e137fa9..d10dfb16a9b8ac 100644 --- a/x-pack/plugins/security_solution/server/graphql/types.ts +++ b/x-pack/plugins/security_solution/server/graphql/types.ts @@ -558,10 +558,6 @@ export interface Source { NetworkDnsHistogram: NetworkDsOverTimeData; NetworkHttp: NetworkHttpData; - - OverviewNetwork?: Maybe; - - OverviewHost?: Maybe; /** Just a simple example to get the app name */ whoAmI?: Maybe; } @@ -1834,64 +1830,6 @@ export interface NetworkHttpItem { statuses: string[]; } -export interface OverviewNetworkData { - auditbeatSocket?: Maybe; - - filebeatCisco?: Maybe; - - filebeatNetflow?: Maybe; - - filebeatPanw?: Maybe; - - filebeatSuricata?: Maybe; - - filebeatZeek?: Maybe; - - packetbeatDNS?: Maybe; - - packetbeatFlow?: Maybe; - - packetbeatTLS?: Maybe; - - inspect?: Maybe; -} - -export interface OverviewHostData { - auditbeatAuditd?: Maybe; - - auditbeatFIM?: Maybe; - - auditbeatLogin?: Maybe; - - auditbeatPackage?: Maybe; - - auditbeatProcess?: Maybe; - - auditbeatUser?: Maybe; - - endgameDns?: Maybe; - - endgameFile?: Maybe; - - endgameImageLoad?: Maybe; - - endgameNetwork?: Maybe; - - endgameProcess?: Maybe; - - endgameRegistry?: Maybe; - - endgameSecurity?: Maybe; - - filebeatSystemModule?: Maybe; - - winlogbeatSecurity?: Maybe; - - winlogbeatMWSysmonOperational?: Maybe; - - inspect?: Maybe; -} - export interface SayMyName { /** The id of the source */ appName: string; @@ -2489,24 +2427,6 @@ export interface NetworkHttpSourceArgs { defaultIndex: string[]; } -export interface OverviewNetworkSourceArgs { - id?: Maybe; - - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; -} -export interface OverviewHostSourceArgs { - id?: Maybe; - - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; -} export interface IndicesExistSourceStatusArgs { defaultIndex: string[]; } @@ -2943,10 +2863,6 @@ export namespace SourceResolvers { NetworkDnsHistogram?: NetworkDnsHistogramResolver; NetworkHttp?: NetworkHttpResolver; - - OverviewNetwork?: OverviewNetworkResolver, TypeParent, TContext>; - - OverviewHost?: OverviewHostResolver, TypeParent, TContext>; /** Just a simple example to get the app name */ whoAmI?: WhoAmIResolver, TypeParent, TContext>; } @@ -3298,36 +3214,6 @@ export namespace SourceResolvers { defaultIndex: string[]; } - export type OverviewNetworkResolver< - R = Maybe, - Parent = Source, - TContext = SiemContext - > = Resolver; - export interface OverviewNetworkArgs { - id?: Maybe; - - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; - } - - export type OverviewHostResolver< - R = Maybe, - Parent = Source, - TContext = SiemContext - > = Resolver; - export interface OverviewHostArgs { - id?: Maybe; - - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; - } - export type WhoAmIResolver< R = Maybe, Parent = Source, @@ -7598,209 +7484,6 @@ export namespace NetworkHttpItemResolvers { > = Resolver; } -export namespace OverviewNetworkDataResolvers { - export interface Resolvers { - auditbeatSocket?: AuditbeatSocketResolver, TypeParent, TContext>; - - filebeatCisco?: FilebeatCiscoResolver, TypeParent, TContext>; - - filebeatNetflow?: FilebeatNetflowResolver, TypeParent, TContext>; - - filebeatPanw?: FilebeatPanwResolver, TypeParent, TContext>; - - filebeatSuricata?: FilebeatSuricataResolver, TypeParent, TContext>; - - filebeatZeek?: FilebeatZeekResolver, TypeParent, TContext>; - - packetbeatDNS?: PacketbeatDnsResolver, TypeParent, TContext>; - - packetbeatFlow?: PacketbeatFlowResolver, TypeParent, TContext>; - - packetbeatTLS?: PacketbeatTlsResolver, TypeParent, TContext>; - - inspect?: InspectResolver, TypeParent, TContext>; - } - - export type AuditbeatSocketResolver< - R = Maybe, - Parent = OverviewNetworkData, - TContext = SiemContext - > = Resolver; - export type FilebeatCiscoResolver< - R = Maybe, - Parent = OverviewNetworkData, - TContext = SiemContext - > = Resolver; - export type FilebeatNetflowResolver< - R = Maybe, - Parent = OverviewNetworkData, - TContext = SiemContext - > = Resolver; - export type FilebeatPanwResolver< - R = Maybe, - Parent = OverviewNetworkData, - TContext = SiemContext - > = Resolver; - export type FilebeatSuricataResolver< - R = Maybe, - Parent = OverviewNetworkData, - TContext = SiemContext - > = Resolver; - export type FilebeatZeekResolver< - R = Maybe, - Parent = OverviewNetworkData, - TContext = SiemContext - > = Resolver; - export type PacketbeatDnsResolver< - R = Maybe, - Parent = OverviewNetworkData, - TContext = SiemContext - > = Resolver; - export type PacketbeatFlowResolver< - R = Maybe, - Parent = OverviewNetworkData, - TContext = SiemContext - > = Resolver; - export type PacketbeatTlsResolver< - R = Maybe, - Parent = OverviewNetworkData, - TContext = SiemContext - > = Resolver; - export type InspectResolver< - R = Maybe, - Parent = OverviewNetworkData, - TContext = SiemContext - > = Resolver; -} - -export namespace OverviewHostDataResolvers { - export interface Resolvers { - auditbeatAuditd?: AuditbeatAuditdResolver, TypeParent, TContext>; - - auditbeatFIM?: AuditbeatFimResolver, TypeParent, TContext>; - - auditbeatLogin?: AuditbeatLoginResolver, TypeParent, TContext>; - - auditbeatPackage?: AuditbeatPackageResolver, TypeParent, TContext>; - - auditbeatProcess?: AuditbeatProcessResolver, TypeParent, TContext>; - - auditbeatUser?: AuditbeatUserResolver, TypeParent, TContext>; - - endgameDns?: EndgameDnsResolver, TypeParent, TContext>; - - endgameFile?: EndgameFileResolver, TypeParent, TContext>; - - endgameImageLoad?: EndgameImageLoadResolver, TypeParent, TContext>; - - endgameNetwork?: EndgameNetworkResolver, TypeParent, TContext>; - - endgameProcess?: EndgameProcessResolver, TypeParent, TContext>; - - endgameRegistry?: EndgameRegistryResolver, TypeParent, TContext>; - - endgameSecurity?: EndgameSecurityResolver, TypeParent, TContext>; - - filebeatSystemModule?: FilebeatSystemModuleResolver, TypeParent, TContext>; - - winlogbeatSecurity?: WinlogbeatSecurityResolver, TypeParent, TContext>; - - winlogbeatMWSysmonOperational?: WinlogbeatMwSysmonOperationalResolver< - Maybe, - TypeParent, - TContext - >; - - inspect?: InspectResolver, TypeParent, TContext>; - } - - export type AuditbeatAuditdResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type AuditbeatFimResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type AuditbeatLoginResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type AuditbeatPackageResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type AuditbeatProcessResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type AuditbeatUserResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type EndgameDnsResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type EndgameFileResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type EndgameImageLoadResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type EndgameNetworkResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type EndgameProcessResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type EndgameRegistryResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type EndgameSecurityResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type FilebeatSystemModuleResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type WinlogbeatSecurityResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type WinlogbeatMwSysmonOperationalResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; - export type InspectResolver< - R = Maybe, - Parent = OverviewHostData, - TContext = SiemContext - > = Resolver; -} - export namespace SayMyNameResolvers { export interface Resolvers { /** The id of the source */ @@ -9168,8 +8851,6 @@ export type IResolvers = { NetworkHttpData?: NetworkHttpDataResolvers.Resolvers; NetworkHttpEdges?: NetworkHttpEdgesResolvers.Resolvers; NetworkHttpItem?: NetworkHttpItemResolvers.Resolvers; - OverviewNetworkData?: OverviewNetworkDataResolvers.Resolvers; - OverviewHostData?: OverviewHostDataResolvers.Resolvers; SayMyName?: SayMyNameResolvers.Resolvers; TimelineResult?: TimelineResultResolvers.Resolvers; ColumnHeaderResult?: ColumnHeaderResultResolvers.Resolvers; diff --git a/x-pack/plugins/security_solution/server/init_server.ts b/x-pack/plugins/security_solution/server/init_server.ts index 7cb2127a3d9d7f..ac0273ec1770de 100644 --- a/x-pack/plugins/security_solution/server/init_server.ts +++ b/x-pack/plugins/security_solution/server/init_server.ts @@ -16,7 +16,6 @@ import { createKpiNetworkResolvers } from './graphql/kpi_network'; import { createNetworkResolvers } from './graphql/network'; import { createNoteResolvers } from './graphql/note'; import { createPinnedEventResolvers } from './graphql/pinned_event'; -import { createOverviewResolvers } from './graphql/overview'; import { createScalarDateResolvers } from './graphql/scalar_date'; import { createScalarToAnyValueResolvers } from './graphql/scalar_to_any'; import { createScalarToBooleanArrayValueResolvers } from './graphql/scalar_to_boolean_array'; @@ -43,7 +42,6 @@ export const initServer = (libs: AppBackendLibs) => { createPinnedEventResolvers(libs) as IResolvers, createSourcesResolvers(libs) as IResolvers, createScalarToStringArrayValueResolvers() as IResolvers, - createOverviewResolvers(libs) as IResolvers, createNetworkResolvers(libs) as IResolvers, createScalarDateResolvers() as IResolvers, createScalarToDateArrayValueResolvers() as IResolvers, diff --git a/x-pack/plugins/security_solution/server/lib/compose/kibana.ts b/x-pack/plugins/security_solution/server/lib/compose/kibana.ts index cfd7bfbf255f60..3bfb3d9492353e 100644 --- a/x-pack/plugins/security_solution/server/lib/compose/kibana.ts +++ b/x-pack/plugins/security_solution/server/lib/compose/kibana.ts @@ -21,8 +21,6 @@ import { ElasticsearchIpDetailsAdapter, IpDetails } from '../ip_details'; 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 } from '../types'; @@ -52,7 +50,6 @@ export function compose( kpiNetwork: new KpiNetwork(new ElasticsearchKpiNetworkAdapter(framework)), matrixHistogram: new MatrixHistogram(new ElasticsearchMatrixHistogramAdapter(framework)), network: new Network(new ElasticsearchNetworkAdapter(framework)), - overview: new Overview(new ElasticsearchOverviewAdapter(framework)), }; const libs: AppBackendLibs = { diff --git a/x-pack/plugins/security_solution/server/lib/overview/elastic_adapter.test.ts b/x-pack/plugins/security_solution/server/lib/overview/elastic_adapter.test.ts deleted file mode 100644 index f421704dffe12e..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/overview/elastic_adapter.test.ts +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { 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, - mockBuildOverviewHostQuery, - mockBuildOverviewNetworkQuery, -} from './mock'; - -jest.mock('./query.dsl', () => { - return { - buildOverviewHostQuery: jest.fn(() => mockBuildOverviewHostQuery), - buildOverviewNetworkQuery: jest.fn(() => mockBuildOverviewNetworkQuery), - }; -}); - -describe('Siem Overview elasticsearch_adapter', () => { - describe('Network Stats', () => { - describe('Happy Path - get Data', () => { - const mockCallWithRequest = jest.fn(); - mockCallWithRequest.mockResolvedValue(mockResponseNetwork); - const mockFramework: FrameworkAdapter = { - callWithRequest: mockCallWithRequest, - registerGraphQLEndpoint: jest.fn(), - getIndexPatternsService: jest.fn(), - }; - jest.doMock('../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; - mockNoDataResponse.aggregations.unique_zeek_count.doc_count = 0; - mockNoDataResponse.aggregations.unique_packetbeat_count.unique_tls_count.doc_count = 0; - mockNoDataResponse.aggregations.unique_filebeat_count.unique_cisco_count.doc_count = 0; - mockNoDataResponse.aggregations.unique_filebeat_count.unique_netflow_count.doc_count = 0; - mockNoDataResponse.aggregations.unique_filebeat_count.unique_panw_count.doc_count = 0; - const mockCallWithRequest = jest.fn(); - mockCallWithRequest.mockResolvedValue(mockNoDataResponse); - const mockFramework: FrameworkAdapter = { - callWithRequest: mockCallWithRequest, - registerGraphQLEndpoint: jest.fn(), - getIndexPatternsService: jest.fn(), - }; - jest.doMock('../framework', () => ({ - callWithRequest: mockCallWithRequest, - })); - - test('getOverviewNetwork', async () => { - const EsOverviewNetwork = new ElasticsearchOverviewAdapter(mockFramework); - const data: OverviewNetworkData = await EsOverviewNetwork.getOverviewNetwork( - mockRequestNetwork as FrameworkRequest, - mockOptionsNetwork - ); - expect(data).toEqual({ - inspect: { - dsl: [JSON.stringify(mockBuildOverviewNetworkQuery, null, 2)], - response: [JSON.stringify(mockNoDataResponse, null, 2)], - }, - auditbeatSocket: 0, - filebeatCisco: 0, - filebeatNetflow: 0, - filebeatPanw: 0, - filebeatSuricata: 0, - filebeatZeek: 0, - packetbeatDNS: 0, - packetbeatFlow: 0, - packetbeatTLS: 0, - }); - }); - }); - }); - describe('Host Stats', () => { - describe('Happy Path - get Data', () => { - const mockCallWithRequest = jest.fn(); - mockCallWithRequest.mockResolvedValue(mockResponseHost); - const mockFramework: FrameworkAdapter = { - callWithRequest: mockCallWithRequest, - registerGraphQLEndpoint: jest.fn(), - getIndexPatternsService: jest.fn(), - }; - jest.doMock('../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.endgame_module.dns_event_count.doc_count = 0; - mockNoDataResponse.aggregations.endgame_module.file_event_count.doc_count = 0; - mockNoDataResponse.aggregations.endgame_module.image_load_event_count.doc_count = 0; - mockNoDataResponse.aggregations.endgame_module.network_event_count.doc_count = 0; - mockNoDataResponse.aggregations.endgame_module.process_event_count.doc_count = 0; - mockNoDataResponse.aggregations.endgame_module.registry_event.doc_count = 0; - mockNoDataResponse.aggregations.endgame_module.security_event_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; - mockNoDataResponse.aggregations.system_module.filebeat_count.doc_count = 0; - mockNoDataResponse.aggregations.winlog_module.security_event_count.doc_count = 0; - mockNoDataResponse.aggregations.winlog_module.mwsysmon_operational_event_count.doc_count = 0; - const mockCallWithRequest = jest.fn(); - mockCallWithRequest.mockResolvedValue(mockNoDataResponse); - const mockFramework: FrameworkAdapter = { - callWithRequest: mockCallWithRequest, - registerGraphQLEndpoint: jest.fn(), - getIndexPatternsService: jest.fn(), - }; - jest.doMock('../framework', () => ({ - callWithRequest: mockCallWithRequest, - })); - - test('getOverviewHost', async () => { - const EsOverviewHost = new ElasticsearchOverviewAdapter(mockFramework); - const data: OverviewHostData = await EsOverviewHost.getOverviewHost( - mockRequestHost as FrameworkRequest, - mockOptionsHost - ); - expect(data).toEqual({ - inspect: { - dsl: [JSON.stringify(mockBuildOverviewHostQuery, null, 2)], - response: [JSON.stringify(mockNoDataResponse, null, 2)], - }, - auditbeatAuditd: 0, - auditbeatFIM: 0, - auditbeatLogin: 0, - auditbeatPackage: 0, - auditbeatProcess: 0, - auditbeatUser: 0, - endgameDns: 0, - endgameFile: 0, - endgameImageLoad: 0, - endgameNetwork: 0, - endgameProcess: 0, - endgameRegistry: 0, - endgameSecurity: 0, - filebeatSystemModule: 0, - winlogbeatSecurity: 0, - winlogbeatMWSysmonOperational: 0, - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/overview/elasticsearch_adapter.ts b/x-pack/plugins/security_solution/server/lib/overview/elasticsearch_adapter.ts deleted file mode 100644 index 982b47110c5135..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/overview/elasticsearch_adapter.ts +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getOr } from 'lodash/fp'; - -import { OverviewHostData, OverviewNetworkData } from '../../graphql/types'; -import { inspectStringifyObject } from '../../utils/build_query'; -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 { - const dsl = buildOverviewNetworkQuery(options); - const response = await this.framework.callWithRequest( - request, - 'search', - dsl - ); - const inspect = { - dsl: [inspectStringifyObject(dsl)], - response: [inspectStringifyObject(response)], - }; - - return { - inspect, - auditbeatSocket: getOr(null, 'aggregations.unique_socket_count.doc_count', response), - filebeatCisco: getOr( - null, - 'aggregations.unique_filebeat_count.unique_cisco_count.doc_count', - response - ), - filebeatNetflow: getOr( - null, - 'aggregations.unique_filebeat_count.unique_netflow_count.doc_count', - response - ), - filebeatPanw: getOr( - null, - 'aggregations.unique_filebeat_count.unique_panw_count.doc_count', - response - ), - filebeatSuricata: getOr(null, 'aggregations.unique_suricata_count.doc_count', response), - filebeatZeek: getOr(null, 'aggregations.unique_zeek_count.doc_count', response), - packetbeatDNS: getOr(null, 'aggregations.unique_dns_count.doc_count', response), - packetbeatFlow: getOr(null, 'aggregations.unique_flow_count.doc_count', response), - packetbeatTLS: getOr( - null, - 'aggregations.unique_packetbeat_count.unique_tls_count.doc_count', - response - ), - }; - } - - public async getOverviewHost( - request: FrameworkRequest, - options: RequestBasicOptions - ): Promise { - const dsl = buildOverviewHostQuery(options); - const response = await this.framework.callWithRequest( - request, - 'search', - dsl - ); - const inspect = { - dsl: [inspectStringifyObject(dsl)], - response: [inspectStringifyObject(response)], - }; - - return { - inspect, - 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), - endgameDns: getOr(null, 'aggregations.endgame_module.dns_event_count.doc_count', response), - endgameFile: getOr(null, 'aggregations.endgame_module.file_event_count.doc_count', response), - endgameImageLoad: getOr( - null, - 'aggregations.endgame_module.image_load_event_count.doc_count', - response - ), - endgameNetwork: getOr( - null, - 'aggregations.endgame_module.network_event_count.doc_count', - response - ), - endgameProcess: getOr( - null, - 'aggregations.endgame_module.process_event_count.doc_count', - response - ), - endgameRegistry: getOr( - null, - 'aggregations.endgame_module.registry_event.doc_count', - response - ), - endgameSecurity: getOr( - null, - 'aggregations.endgame_module.security_event_count.doc_count', - response - ), - filebeatSystemModule: getOr( - null, - 'aggregations.system_module.filebeat_count.doc_count', - response - ), - winlogbeatSecurity: getOr( - null, - 'aggregations.winlog_module.security_event_count.doc_count', - response - ), - winlogbeatMWSysmonOperational: getOr( - null, - 'aggregations.winlog_module.mwsysmon_operational_event_count.doc_count', - response - ), - }; - } -} diff --git a/x-pack/plugins/security_solution/server/lib/overview/index.ts b/x-pack/plugins/security_solution/server/lib/overview/index.ts deleted file mode 100644 index ae9f81eb261a75..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/overview/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { 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 { - return this.adapter.getOverviewNetwork(req, options); - } - - public async getOverviewHost( - req: FrameworkRequest, - options: RequestBasicOptions - ): Promise { - return this.adapter.getOverviewHost(req, options); - } -} diff --git a/x-pack/plugins/security_solution/server/lib/overview/mock.ts b/x-pack/plugins/security_solution/server/lib/overview/mock.ts deleted file mode 100644 index 2621c795ecd6b8..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/overview/mock.ts +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { DEFAULT_INDEX_PATTERN } from '../../../common/constants'; -import { RequestBasicOptions } from '../framework/types'; - -export const mockOptionsNetwork: RequestBasicOptions = { - defaultIndex: DEFAULT_INDEX_PATTERN, - sourceConfiguration: { - fields: { - container: 'docker.container.name', - host: 'beat.hostname', - message: ['message', '@message'], - pod: 'kubernetes.pod.name', - tiebreaker: '_doc', - timestamp: '@timestamp', - }, - }, - timerange: { interval: '12h', to: '2019-02-11T02:26:46.071Z', from: '2019-02-10T02:26:46.071Z' }, - filterQuery: {}, -}; - -export const mockRequestNetwork = { - body: { - operationName: 'GetOverviewNetworkQuery', - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - from: '2019-02-10T02:30:30.772Z', - to: '2019-02-11T02:30:30.772Z', - }, - 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 }', - }, -}; - -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 }, - unique_filebeat_count: { - doc_count: 456756, - unique_cisco_count: { doc_count: 14 }, - unique_netflow_count: { doc_count: 992 }, - unique_panw_count: { doc_count: 225 }, - }, - unique_packetbeat_count: { doc_count: 7897896, unique_tls_count: { doc_count: 2009 } }, - }, -}; - -export const mockBuildOverviewHostQuery = { buildOverviewHostQuery: 'buildOverviewHostQuery' }; -export const mockBuildOverviewNetworkQuery = { - buildOverviewNetworkQuery: 'buildOverviewNetworkQuery', -}; - -export const mockResultNetwork = { - inspect: { - dsl: [JSON.stringify(mockBuildOverviewNetworkQuery, null, 2)], - response: [JSON.stringify(mockResponseNetwork, null, 2)], - }, - packetbeatFlow: 50243, - packetbeatDNS: 15000, - filebeatSuricata: 2375, - filebeatZeek: 456, - auditbeatSocket: 13, - filebeatCisco: 14, - filebeatNetflow: 992, - filebeatPanw: 225, - packetbeatTLS: 2009, -}; - -export const mockOptionsHost: RequestBasicOptions = { - defaultIndex: DEFAULT_INDEX_PATTERN, - sourceConfiguration: { - fields: { - container: 'docker.container.name', - host: 'beat.hostname', - message: ['message', '@message'], - pod: 'kubernetes.pod.name', - tiebreaker: '_doc', - timestamp: '@timestamp', - }, - }, - timerange: { interval: '12h', to: '2019-02-11T02:26:46.071Z', from: '2019-02-10T02:26:46.071Z' }, - filterQuery: {}, -}; - -export const mockRequestHost = { - body: { - operationName: 'GetOverviewHostQuery', - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - from: '2019-02-10T02:30:30.772Z', - to: '2019-02-11T02:30:30.772Z', - }, - 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 }', - }, -}; - -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 }, - endgame_module: { - doc_count: 6258, - dns_event_count: { doc_count: 891 }, - file_event_count: { doc_count: 892 }, - image_load_event_count: { doc_count: 893 }, - network_event_count: { doc_count: 894 }, - process_event_count: { doc_count: 895 }, - registry_event: { doc_count: 896 }, - security_event_count: { doc_count: 897 }, - }, - 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 }, - filebeat_count: { doc_count: 225 }, - }, - winlog_module: { - security_event_count: { - doc_count: 523, - }, - mwsysmon_operational_event_count: { - doc_count: 214, - }, - }, - }, -}; - -export const mockResultHost = { - inspect: { - dsl: [JSON.stringify(mockBuildOverviewHostQuery, null, 2)], - response: [JSON.stringify(mockResponseHost, null, 2)], - }, - auditbeatAuditd: 73847, - auditbeatFIM: 107307, - auditbeatLogin: 60015, - auditbeatPackage: 2003, - auditbeatProcess: 1200, - auditbeatUser: 1979, - endgameDns: 891, - endgameFile: 892, - endgameImageLoad: 893, - endgameNetwork: 894, - endgameProcess: 895, - endgameRegistry: 896, - endgameSecurity: 897, - filebeatSystemModule: 225, - winlogbeatSecurity: 523, - winlogbeatMWSysmonOperational: 214, -}; diff --git a/x-pack/plugins/security_solution/server/lib/overview/query.dsl.ts b/x-pack/plugins/security_solution/server/lib/overview/query.dsl.ts deleted file mode 100644 index b6b1cfea394fd1..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/overview/query.dsl.ts +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { createQueryFilterClauses } from '../../utils/build_query'; -import { RequestBasicOptions } from '../framework'; - -export const buildOverviewNetworkQuery = ({ - filterQuery, - timerange: { from, to }, - defaultIndex, - sourceConfiguration: { - fields: { timestamp }, - }, -}: RequestBasicOptions) => { - const filter = [ - ...createQueryFilterClauses(filterQuery), - { - range: { - [timestamp]: { - gte: from, - lte: to, - format: 'strict_date_optional_time', - }, - }, - }, - ]; - - const dslQuery = { - allowNoIndices: true, - index: defaultIndex, - 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' }, - }, - }, - unique_filebeat_count: { - filter: { - term: { 'agent.type': 'filebeat' }, - }, - aggs: { - unique_netflow_count: { - filter: { - term: { 'input.type': 'netflow' }, - }, - }, - unique_panw_count: { - filter: { - term: { 'event.module': 'panw' }, - }, - }, - unique_cisco_count: { - filter: { - term: { 'event.module': 'cisco' }, - }, - }, - }, - }, - unique_packetbeat_count: { - filter: { - term: { 'agent.type': 'packetbeat' }, - }, - aggs: { - unique_tls_count: { - filter: { - term: { 'network.protocol': 'tls' }, - }, - }, - }, - }, - }, - query: { - bool: { - filter, - }, - }, - size: 0, - track_total_hits: false, - }, - }; - - return dslQuery; -}; - -export const buildOverviewHostQuery = ({ - filterQuery, - timerange: { from, to }, - defaultIndex, - sourceConfiguration: { - fields: { timestamp }, - }, -}: RequestBasicOptions) => { - const filter = [ - ...createQueryFilterClauses(filterQuery), - { - range: { - [timestamp]: { - gte: from, - lte: to, - format: 'strict_date_optional_time', - }, - }, - }, - ]; - - const dslQuery = { - allowNoIndices: true, - index: defaultIndex, - ignoreUnavailable: true, - body: { - aggregations: { - auditd_count: { - filter: { - term: { - 'event.module': 'auditd', - }, - }, - }, - endgame_module: { - filter: { - bool: { - should: [ - { - term: { 'event.module': 'endpoint' }, - }, - { - term: { - 'event.module': 'endgame', - }, - }, - ], - }, - }, - aggs: { - dns_event_count: { - filter: { - bool: { - should: [ - { - bool: { - filter: [ - { term: { 'network.protocol': 'dns' } }, - { term: { 'event.category': 'network' } }, - ], - }, - }, - { - term: { - 'endgame.event_type_full': 'dns_event', - }, - }, - ], - }, - }, - }, - file_event_count: { - filter: { - bool: { - should: [ - { - term: { - 'event.category': 'file', - }, - }, - { - term: { - 'endgame.event_type_full': 'file_event', - }, - }, - ], - }, - }, - }, - image_load_event_count: { - filter: { - bool: { - should: [ - { - bool: { - should: [ - { - term: { - 'event.category': 'library', - }, - }, - { - term: { - 'event.category': 'driver', - }, - }, - ], - }, - }, - { - term: { - 'endgame.event_type_full': 'image_load_event', - }, - }, - ], - }, - }, - }, - network_event_count: { - filter: { - bool: { - should: [ - { - bool: { - filter: [ - { - bool: { - must_not: { - term: { 'network.protocol': 'dns' }, - }, - }, - }, - { - term: { 'event.category': 'network' }, - }, - ], - }, - }, - { - term: { - 'endgame.event_type_full': 'network_event', - }, - }, - ], - }, - }, - }, - process_event_count: { - filter: { - bool: { - should: [ - { - term: { 'event.category': 'process' }, - }, - { - term: { - 'endgame.event_type_full': 'process_event', - }, - }, - ], - }, - }, - }, - registry_event: { - filter: { - bool: { - should: [ - { - term: { 'event.category': 'registry' }, - }, - { - term: { - 'endgame.event_type_full': 'registry_event', - }, - }, - ], - }, - }, - }, - security_event_count: { - filter: { - bool: { - should: [ - { - bool: { - filter: [ - { term: { 'event.category': 'session' } }, - { term: { 'event.category': 'authentication' } }, - ], - }, - }, - { - term: { - 'endgame.event_type_full': 'security_event', - }, - }, - ], - }, - }, - }, - }, - }, - fim_count: { - filter: { - term: { - 'event.module': 'file_integrity', - }, - }, - }, - winlog_module: { - filter: { - term: { - 'agent.type': 'winlogbeat', - }, - }, - aggs: { - mwsysmon_operational_event_count: { - filter: { - term: { - 'winlog.channel': 'Microsoft-Windows-Sysmon/Operational', - }, - }, - }, - security_event_count: { - filter: { - term: { - 'winlog.channel': 'Security', - }, - }, - }, - }, - }, - 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', - }, - }, - }, - filebeat_count: { - filter: { - term: { - 'agent.type': 'filebeat', - }, - }, - }, - }, - }, - }, - query: { - bool: { - filter, - }, - }, - size: 0, - track_total_hits: false, - }, - }; - - return dslQuery; -}; diff --git a/x-pack/plugins/security_solution/server/lib/overview/types.ts b/x-pack/plugins/security_solution/server/lib/overview/types.ts deleted file mode 100644 index 7fdad08ac9b379..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/overview/types.ts +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { OverviewHostData, OverviewNetworkData } from '../../graphql/types'; -import { FrameworkRequest, RequestBasicOptions } from '../framework'; -import { SearchHit } from '../types'; - -export interface OverviewAdapter { - getOverviewNetwork( - request: FrameworkRequest, - options: RequestBasicOptions - ): Promise; - getOverviewHost( - request: FrameworkRequest, - options: RequestBasicOptions - ): Promise; -} - -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; - }; - unique_filebeat_count: { - unique_netflow_count: { - doc_count: number; - }; - unique_panw_count: { - doc_count: number; - }; - unique_cisco_count: { - doc_count: number; - }; - }; - unique_packetbeat_count: { - unique_tls_count: { - doc_count: number; - }; - }; - }; -} - -export interface OverviewHostHit extends SearchHit { - aggregations: { - auditd_count: { - doc_count: number; - }; - endgame_module: { - dns_event_count: { - doc_count: number; - }; - file_event_count: { - doc_count: number; - }; - image_load_event_count: { - doc_count: number; - }; - network_event_count: { - doc_count: number; - }; - process_event_count: { - doc_count: number; - }; - registry_event: { - doc_count: number; - }; - security_event_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; - }; - filebeat_count: { - doc_count: number; - }; - }; - winlog_count: { - doc_count: number; - }; - }; -} diff --git a/x-pack/plugins/security_solution/server/lib/types.ts b/x-pack/plugins/security_solution/server/lib/types.ts index 87e755360285fb..3c7c1cd3d7cff9 100644 --- a/x-pack/plugins/security_solution/server/lib/types.ts +++ b/x-pack/plugins/security_solution/server/lib/types.ts @@ -17,7 +17,6 @@ import { IpDetails } from './ip_details'; import { KpiHosts } from './kpi_hosts'; import { KpiNetwork } from './kpi_network'; import { Network } from './network'; -import { Overview } from './overview'; import { SourceStatus } from './source_status'; import { Sources } from './sources'; import { Note } from './note/saved_object'; @@ -36,7 +35,6 @@ export interface AppDomainLibs { matrixHistogram: MatrixHistogram; network: Network; kpiNetwork: KpiNetwork; - overview: Overview; kpiHosts: KpiHosts; } diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/index.ts index 7a28c983ec466d..61c228a5fd164d 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/index.ts @@ -8,7 +8,7 @@ import { get, getOr } from 'lodash/fp'; import { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; import { - HostOverviewStrategyResponse, + HostsOverviewStrategyResponse, HostsQueries, HostOverviewRequestOptions, OverviewHostHit, @@ -22,7 +22,7 @@ export const hostOverview: SecuritySolutionFactory = { parse: async ( options: HostOverviewRequestOptions, response: IEsSearchResponse - ): Promise => { + ): Promise => { const aggregations: OverviewHostHit = get('aggregations', response.rawResponse) || {}; const inspect = { dsl: [inspectStringifyObject(buildOverviewHostQuery(options))], diff --git a/x-pack/test/api_integration/apis/security_solution/index.js b/x-pack/test/api_integration/apis/security_solution/index.js index 16a38c0fafbca9..a9ddf091245f79 100644 --- a/x-pack/test/api_integration/apis/security_solution/index.js +++ b/x-pack/test/api_integration/apis/security_solution/index.js @@ -12,12 +12,12 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./kpi_hosts')); loadTestFile(require.resolve('./network_dns')); loadTestFile(require.resolve('./network_top_n_flow')); - loadTestFile(require.resolve('./overview_host')); + // loadTestFile(require.resolve('./overview_host')); loadTestFile(require.resolve('./saved_objects/notes')); loadTestFile(require.resolve('./saved_objects/pinned_events')); loadTestFile(require.resolve('./saved_objects/timeline')); loadTestFile(require.resolve('./sources')); - loadTestFile(require.resolve('./overview_network')); + // loadTestFile(require.resolve('./overview_network')); loadTestFile(require.resolve('./timeline')); loadTestFile(require.resolve('./timeline_details')); // loadTestFile(require.resolve('./uncommon_processes')); diff --git a/x-pack/test/api_integration/apis/security_solution/overview_host.ts b/x-pack/test/api_integration/apis/security_solution/overview_host.ts index ffbf9d89fc112e..0d648e665a9a9e 100644 --- a/x-pack/test/api_integration/apis/security_solution/overview_host.ts +++ b/x-pack/test/api_integration/apis/security_solution/overview_host.ts @@ -7,7 +7,9 @@ import expect from '@kbn/expect'; import { DEFAULT_INDEX_PATTERN } from '../../../../plugins/security_solution/common/constants'; +// @ts-expect-error import { overviewHostQuery } from '../../../../plugins/security_solution/public/overview/containers//overview_host/index.gql_query'; +// @ts-expect-error import { GetOverviewHostQuery } from '../../../../plugins/security_solution/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; diff --git a/x-pack/test/api_integration/apis/security_solution/overview_network.ts b/x-pack/test/api_integration/apis/security_solution/overview_network.ts index 6976b225a4d2ad..60d300e168e4ad 100644 --- a/x-pack/test/api_integration/apis/security_solution/overview_network.ts +++ b/x-pack/test/api_integration/apis/security_solution/overview_network.ts @@ -5,7 +5,9 @@ */ import expect from '@kbn/expect'; +// @ts-expect-error import { overviewNetworkQuery } from '../../../../plugins/security_solution/public/overview/containers/overview_network/index.gql_query'; +// @ts-expect-error import { GetOverviewNetworkQuery } from '../../../../plugins/security_solution/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; From a00b3ee671b5cbb22b9efc6afb8d589744063874 Mon Sep 17 00:00:00 2001 From: Sandra Gonzales Date: Wed, 23 Sep 2020 16:49:26 -0400 Subject: [PATCH 20/35] [Ingest Manager] add upgrade action (#77412) * at agents/agent-id/upgrade endpoint * handle upgrade ack * let upgrade endpoint accept url and version * wrap action data in data prop * decrypt data of actions * type upgrade action and decrypt data in ack * error if trying to update to diff version of kibana * add some integration tests * untype * fix test * update integration test * reset upgraded_at when upgrading * use defaultIngestErrorHandler * use ack_data instead of data * copy data to ack_data --- .../ingest_manager/common/constants/routes.ts | 1 + .../common/services/agent_status.ts | 3 + .../common/types/models/agent.ts | 7 ++- .../common/types/rest_spec/agent.ts | 7 +++ .../server/routes/agent/index.ts | 12 +++- .../server/routes/agent/upgrade_handler.ts | 47 ++++++++++++++ .../server/saved_objects/index.ts | 2 + .../server/services/agents/acks.ts | 6 ++ .../server/services/agents/actions.ts | 35 +++++++++-- .../server/services/agents/index.ts | 1 + .../server/services/agents/upgrade.ts | 61 +++++++++++++++++++ .../server/types/models/agent.ts | 7 ++- .../server/types/rest_spec/agent.ts | 10 +++ .../apis/fleet/agents/acks.ts | 46 ++++++++++++++ .../apis/fleet/agents/upgrade.ts | 57 +++++++++++++++++ .../apis/fleet/index.js | 1 + 16 files changed, 292 insertions(+), 11 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/server/routes/agent/upgrade_handler.ts create mode 100644 x-pack/plugins/ingest_manager/server/services/agents/upgrade.ts create mode 100644 x-pack/test/ingest_manager_api_integration/apis/fleet/agents/upgrade.ts diff --git a/x-pack/plugins/ingest_manager/common/constants/routes.ts b/x-pack/plugins/ingest_manager/common/constants/routes.ts index d899739a74ef00..69672dfb9ec6cc 100644 --- a/x-pack/plugins/ingest_manager/common/constants/routes.ts +++ b/x-pack/plugins/ingest_manager/common/constants/routes.ts @@ -90,6 +90,7 @@ export const AGENT_API_ROUTES = { REASSIGN_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/reassign`, BULK_REASSIGN_PATTERN: `${FLEET_API_ROOT}/agents/bulk_reassign`, STATUS_PATTERN: `${FLEET_API_ROOT}/agent-status`, + UPGRADE_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/upgrade`, }; export const ENROLLMENT_API_KEY_ROUTES = { diff --git a/x-pack/plugins/ingest_manager/common/services/agent_status.ts b/x-pack/plugins/ingest_manager/common/services/agent_status.ts index fe4e094e1bb229..70f4d7f9344f97 100644 --- a/x-pack/plugins/ingest_manager/common/services/agent_status.ts +++ b/x-pack/plugins/ingest_manager/common/services/agent_status.ts @@ -19,6 +19,9 @@ export function getAgentStatus(agent: Agent, now: number = Date.now()): AgentSta if (!agent.last_checkin) { return 'enrolling'; } + if (agent.upgrade_started_at && !agent.upgraded_at) { + return 'upgrading'; + } const msLastCheckIn = new Date(lastCheckIn || 0).getTime(); const msSinceLastCheckIn = new Date().getTime() - msLastCheckIn; diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index a204373fe2e56b..7110fd4ce52eac 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -19,10 +19,10 @@ export type AgentStatus = | 'warning' | 'enrolling' | 'unenrolling' + | 'upgrading' | 'degraded'; -export type AgentActionType = 'CONFIG_CHANGE' | 'UNENROLL'; - +export type AgentActionType = 'CONFIG_CHANGE' | 'UNENROLL' | 'UPGRADE'; export interface NewAgentAction { type: AgentActionType; data?: any; @@ -65,7 +65,6 @@ export type AgentPolicyActionSOAttributes = CommonAgentActionSOAttributes & { policy_id: string; policy_revision: number; }; - export type BaseAgentActionSOAttributes = AgentActionSOAttributes | AgentPolicyActionSOAttributes; export interface NewAgentEvent { @@ -110,6 +109,8 @@ interface AgentBase { enrolled_at: string; unenrolled_at?: string; unenrollment_started_at?: string; + upgraded_at?: string; + upgrade_started_at?: string; shared_id?: string; access_api_key_id?: string; default_api_key?: string; diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts index 1a10d4930656f6..ab4c372c4e1d61 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts @@ -113,6 +113,11 @@ export interface PostAgentUnenrollRequest { // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface PostAgentUnenrollResponse {} +export interface PostAgentUpgradeRequest { + params: { + agentId: string; + }; +} export interface PostBulkAgentUnenrollRequest { body: { agents: string[] | string; @@ -120,6 +125,8 @@ export interface PostBulkAgentUnenrollRequest { }; } +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PostAgentUpgradeResponse {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface PostBulkAgentUnenrollResponse {} diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts index eafc726ea166d2..73ed276ba02e74 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts @@ -29,6 +29,7 @@ import { PutAgentReassignRequestSchema, PostBulkAgentReassignRequestSchema, PostAgentEnrollRequestBodyJSONSchema, + PostAgentUpgradeRequestSchema, } from '../../types'; import { getAgentsHandler, @@ -48,6 +49,7 @@ import { postNewAgentActionHandlerBuilder } from './actions_handlers'; import { appContextService } from '../../services'; import { postAgentUnenrollHandler, postBulkAgentsUnenrollHandler } from './unenroll_handler'; import { IngestManagerConfigType } from '../..'; +import { postAgentUpgradeHandler } from './upgrade_handler'; const ajv = new Ajv({ coerceTypes: true, @@ -215,7 +217,15 @@ export const registerRoutes = (router: IRouter, config: IngestManagerConfigType) }, getAgentStatusForAgentPolicyHandler ); - + // upgrade agent + router.post( + { + path: AGENT_API_ROUTES.UPGRADE_PATTERN, + validate: PostAgentUpgradeRequestSchema, + options: { tags: [`access:${PLUGIN_ID}-all`] }, + }, + postAgentUpgradeHandler + ); // Bulk reassign router.post( { diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/upgrade_handler.ts b/x-pack/plugins/ingest_manager/server/routes/agent/upgrade_handler.ts new file mode 100644 index 00000000000000..e5d7a44c007682 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/routes/agent/upgrade_handler.ts @@ -0,0 +1,47 @@ +/* + * 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 { RequestHandler } from 'src/core/server'; +import { TypeOf } from '@kbn/config-schema'; +import { PostAgentUpgradeResponse } from '../../../common/types'; +import { PostAgentUpgradeRequestSchema } from '../../types'; +import * as AgentService from '../../services/agents'; +import { appContextService } from '../../services'; +import { defaultIngestErrorHandler } from '../../errors'; + +export const postAgentUpgradeHandler: RequestHandler< + TypeOf, + undefined, + TypeOf +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + const { version, source_uri: sourceUri } = request.body; + + // temporarily only allow upgrading to the same version as the installed kibana version + const kibanaVersion = appContextService.getKibanaVersion(); + if (kibanaVersion !== version) { + return response.customError({ + statusCode: 400, + body: { + message: `cannot upgrade agent to ${version} because it is different than the installed kibana version ${kibanaVersion}`, + }, + }); + } + + try { + await AgentService.sendUpgradeAgentAction({ + soClient, + agentId: request.params.agentId, + version, + sourceUri, + }); + + const body: PostAgentUpgradeResponse = {}; + return response.ok({ body }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/ingest_manager/server/saved_objects/index.ts b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts index e86f7b24e2c784..fd08b76a3916b3 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects/index.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts @@ -68,6 +68,8 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = { enrolled_at: { type: 'date' }, unenrolled_at: { type: 'date' }, unenrollment_started_at: { type: 'date' }, + upgraded_at: { type: 'date' }, + upgrade_started_at: { type: 'date' }, access_api_key_id: { type: 'keyword' }, version: { type: 'keyword' }, user_provided_metadata: { type: 'flattened' }, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts index 1392710eb0eff4..e22ee4256b0e29 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts @@ -28,6 +28,7 @@ import { } from '../../constants'; import { getAgentActionByIds } from './actions'; import { forceUnenrollAgent } from './unenroll'; +import { ackAgentUpgraded } from './upgrade'; const ALLOWED_ACKNOWLEDGEMENT_TYPE: string[] = ['ACTION_RESULT']; @@ -80,6 +81,11 @@ export async function acknowledgeAgentActions( await forceUnenrollAgent(soClient, agent.id); } + const upgradeAction = actions.find((action) => action.type === 'UPGRADE'); + if (upgradeAction) { + await ackAgentUpgraded(soClient, upgradeAction); + } + const configChangeAction = getLatestConfigChangePolicyActionIfUpdated(agent, actions); await soClient.bulkUpdate([ diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts index 1d4db44edf88ae..f018eea61e4f31 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts @@ -225,7 +225,11 @@ export async function getAgentPolicyActionByIds( ); } -export async function getNewActionsSince(soClient: SavedObjectsClientContract, timestamp: string) { +export async function getNewActionsSince( + soClient: SavedObjectsClientContract, + timestamp: string, + decryptData: boolean = true +) { const filter = nodeTypes.function.buildNode('and', [ nodeTypes.function.buildNode( 'not', @@ -243,14 +247,33 @@ export async function getNewActionsSince(soClient: SavedObjectsClientContract, t } ), ]); - const res = await soClient.find({ - type: AGENT_ACTION_SAVED_OBJECT_TYPE, - filter, - }); - return res.saved_objects + const actions = ( + await soClient.find({ + type: AGENT_ACTION_SAVED_OBJECT_TYPE, + filter, + }) + ).saved_objects .filter(isAgentActionSavedObject) .map((so) => savedObjectToAgentAction(so)); + + if (!decryptData) { + return actions; + } + + return await Promise.all( + actions.map(async (action) => { + // Get decrypted actions + return savedObjectToAgentAction( + await appContextService + .getEncryptedSavedObjects() + .getDecryptedAsInternalUser( + AGENT_ACTION_SAVED_OBJECT_TYPE, + action.id + ) + ); + }) + ); } export async function getLatestConfigChangeAction( diff --git a/x-pack/plugins/ingest_manager/server/services/agents/index.ts b/x-pack/plugins/ingest_manager/server/services/agents/index.ts index 400c099af4e936..c878b666bde886 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/index.ts @@ -9,6 +9,7 @@ export * from './events'; export * from './checkin'; export * from './enroll'; export * from './unenroll'; +export * from './upgrade'; export * from './status'; export * from './crud'; export * from './update'; diff --git a/x-pack/plugins/ingest_manager/server/services/agents/upgrade.ts b/x-pack/plugins/ingest_manager/server/services/agents/upgrade.ts new file mode 100644 index 00000000000000..cee3bc69f25dba --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/agents/upgrade.ts @@ -0,0 +1,61 @@ +/* + * 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 { SavedObjectsClientContract } from 'src/core/server'; +import { AgentSOAttributes, AgentAction, AgentActionSOAttributes } from '../../types'; +import { AGENT_ACTION_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE } from '../../constants'; +import { createAgentAction } from './actions'; + +export async function sendUpgradeAgentAction({ + soClient, + agentId, + version, + sourceUri, +}: { + soClient: SavedObjectsClientContract; + agentId: string; + version: string; + sourceUri: string; +}) { + const now = new Date().toISOString(); + const data = { + version, + source_uri: sourceUri, + }; + await createAgentAction(soClient, { + agent_id: agentId, + created_at: now, + data, + ack_data: data, + type: 'UPGRADE', + }); + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agentId, { + upgraded_at: undefined, + upgrade_started_at: now, + }); +} + +export async function ackAgentUpgraded( + soClient: SavedObjectsClientContract, + agentAction: AgentAction +) { + const { + attributes: { ack_data: ackData }, + } = await soClient.get(AGENT_ACTION_SAVED_OBJECT_TYPE, agentAction.id); + if (!ackData) throw new Error('data missing from UPGRADE action'); + const { version } = JSON.parse(ackData); + if (!version) throw new Error('version missing from UPGRADE action'); + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agentAction.agent_id, { + upgraded_at: new Date().toISOString(), + local_metadata: { + elastic: { + agent: { + version, + }, + }, + }, + }); +} diff --git a/x-pack/plugins/ingest_manager/server/types/models/agent.ts b/x-pack/plugins/ingest_manager/server/types/models/agent.ts index b249705fe6c2fd..15004e60a6fa46 100644 --- a/x-pack/plugins/ingest_manager/server/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/models/agent.ts @@ -62,7 +62,12 @@ export const AgentEventSchema = schema.object({ }); export const NewAgentActionSchema = schema.object({ - type: schema.oneOf([schema.literal('CONFIG_CHANGE'), schema.literal('UNENROLL')]), + type: schema.oneOf([ + schema.literal('CONFIG_CHANGE'), + schema.literal('UNENROLL'), + schema.literal('UPGRADE'), + ]), data: schema.maybe(schema.any()), + ack_data: schema.maybe(schema.any()), sent_at: schema.maybe(schema.string()), }); diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts index 4aefa56e0ca0a1..3866ef095563e4 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts @@ -172,6 +172,16 @@ export const PostAgentUnenrollRequestSchema = { ), }; +export const PostAgentUpgradeRequestSchema = { + params: schema.object({ + agentId: schema.string(), + }), + body: schema.object({ + source_uri: schema.string(), + version: schema.string(), + }), +}; + export const PostBulkAgentUnenrollRequestSchema = { body: schema.object({ agents: schema.oneOf([schema.arrayOf(schema.string()), schema.string()]), diff --git a/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/acks.ts b/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/acks.ts index 1613ca9d11ee66..360b91203dfc87 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/acks.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/acks.ts @@ -13,6 +13,7 @@ export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; const esArchiver = getService('esArchiver'); const esClient = getService('es'); + const kibanaServer = getService('kibanaServer'); const supertest = getSupertestWithoutAuth(providerContext); let apiKey: { id: string; api_key: string }; @@ -205,5 +206,50 @@ export default function (providerContext: FtrProviderContext) { 'ACTION not allowed for acknowledgment only ACTION_RESULT' ); }); + + it('ack upgrade should update fleet-agent SO', async () => { + const { body: actionRes } = await supertest + .post(`/api/ingest_manager/fleet/agents/agent1/actions`) + .set('kbn-xsrf', 'xx') + .set( + 'Authorization', + `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` + ) + .send({ + action: { + type: 'UPGRADE', + ack_data: { version: '8.0.0', source_uri: 'http://localhost:8000' }, + }, + }) + .expect(200); + const actionId = actionRes.item.id; + await supertest + .post(`/api/ingest_manager/fleet/agents/agent1/acks`) + .set('kbn-xsrf', 'xx') + .set( + 'Authorization', + `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` + ) + .send({ + events: [ + { + type: 'ACTION_RESULT', + subtype: 'ACKNOWLEDGED', + timestamp: '2020-09-21T13:25:29.02838-04:00', + action_id: actionId, + agent_id: 'agent1', + message: + "Action '70d97288-ffd9-4549-8c49-2423a844f67f' of type 'UPGRADE' acknowledged.", + }, + ], + }) + .expect(200); + + const res = await kibanaServer.savedObjects.get({ + type: 'fleet-agents', + id: 'agent1', + }); + expect(res.attributes.upgraded_at).to.be.ok(); + }); }); } diff --git a/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/upgrade.ts b/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/upgrade.ts new file mode 100644 index 00000000000000..a783f806c03ee8 --- /dev/null +++ b/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/upgrade.ts @@ -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 expect from '@kbn/expect/expect.js'; +import { FtrProviderContext } from '../../../../api_integration/ftr_provider_context'; +import { setupIngest } from './services'; +import { skipIfNoDockerRegistry } from '../../../helpers'; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + + describe('fleet upgrade agent', () => { + skipIfNoDockerRegistry(providerContext); + setupIngest(providerContext); + before(async () => { + await esArchiver.loadIfNeeded('fleet/agents'); + }); + after(async () => { + await esArchiver.unload('fleet/agents'); + }); + + it('should respond 200 to upgrade agent and update the agent SO', async () => { + const kibanaVersionAccessor = kibanaServer.version; + const kibanaVersion = await kibanaVersionAccessor.get(); + await supertest + .post(`/api/ingest_manager/fleet/agents/agent1/upgrade`) + .set('kbn-xsrf', 'xxx') + .send({ + version: kibanaVersion, + source_uri: 'http://path/to/download', + }) + .expect(200); + const res = await kibanaServer.savedObjects.get({ + type: 'fleet-agents', + id: 'agent1', + }); + expect(res.attributes.upgrade_started_at).to.be.ok(); + }); + + it('should respond 400 if trying to upgrade to a version that does not match installed kibana version', async () => { + await supertest + .post(`/api/ingest_manager/fleet/agents/agent1/upgrade`) + .set('kbn-xsrf', 'xxx') + .send({ + version: '8.0.1', + source_uri: 'http://path/to/download', + }) + .expect(400); + }); + }); +} diff --git a/x-pack/test/ingest_manager_api_integration/apis/fleet/index.js b/x-pack/test/ingest_manager_api_integration/apis/fleet/index.js index 96b9ffd1b04c09..9eb51bee3c7dde 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/fleet/index.js +++ b/x-pack/test/ingest_manager_api_integration/apis/fleet/index.js @@ -18,6 +18,7 @@ export default function loadTests({ loadTestFile }) { loadTestFile(require.resolve('./enrollment_api_keys/crud')); loadTestFile(require.resolve('./install')); loadTestFile(require.resolve('./agents/actions')); + loadTestFile(require.resolve('./agents/upgrade')); loadTestFile(require.resolve('./agents/reassign')); }); } From 32abbff6858122fc86780e11af53e2e6af18370b Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Wed, 23 Sep 2020 17:40:52 -0400 Subject: [PATCH 21/35] [Time to Visualize] Lens By Value With AttributeService (#77561) Used the attribute service to make lens work properly with by value embeddables. --- .../actions/library_notification_action.tsx | 1 + .../application/dashboard_app_controller.tsx | 55 +- ...ce_mock.tsx => attribute_service.mock.tsx} | 0 .../attribute_service.test.ts | 2 +- .../attribute_service/attribute_service.tsx | 17 +- .../public/attribute_service/index.ts | 20 + src/plugins/dashboard/public/index.ts | 4 +- src/plugins/dashboard/public/mocks.tsx | 1 + src/plugins/dashboard/public/plugin.tsx | 2 +- .../lib/actions/edit_panel_action.test.tsx | 42 +- .../public/lib/actions/edit_panel_action.ts | 12 +- .../public/lib/containers/container.ts | 2 +- .../embeddable_state_transfer.test.ts | 27 +- .../public/lib/state_transfer/types.ts | 30 +- .../saved_object_save_modal_origin.tsx | 14 +- .../public/wizard/new_vis_modal.tsx | 4 +- .../public/wizard/show_new_vis.tsx | 1 + .../application/utils/get_top_nav_config.tsx | 16 +- x-pack/plugins/lens/common/constants.ts | 5 +- .../lens/public/app_plugin/app.test.tsx | 992 +++++++++--------- x-pack/plugins/lens/public/app_plugin/app.tsx | 866 +++++++-------- .../lens/public/app_plugin/lens_top_nav.tsx | 73 ++ .../lens/public/app_plugin/mounter.tsx | 146 ++- .../plugins/lens/public/app_plugin/types.ts | 102 ++ .../editor_frame_service/editor_frame/save.ts | 2 +- .../editor_frame/state_management.test.ts | 2 +- .../editor_frame/state_management.ts | 2 +- .../embeddable/embeddable.test.tsx | 240 +++-- .../embeddable/embeddable.tsx | 196 ++-- .../embeddable/embeddable_factory.ts | 74 +- .../editor_frame_service/service.test.tsx | 6 +- .../public/editor_frame_service/service.tsx | 12 +- .../lens/public/lens_attribute_service.ts | 52 + .../persistence/saved_object_store.test.ts | 6 +- .../public/persistence/saved_object_store.ts | 30 +- x-pack/plugins/lens/public/plugin.ts | 33 +- .../routes/maps_app/top_nav_config.tsx | 12 +- 37 files changed, 1783 insertions(+), 1318 deletions(-) rename src/plugins/dashboard/public/attribute_service/{attribute_service_mock.tsx => attribute_service.mock.tsx} (100%) create mode 100644 src/plugins/dashboard/public/attribute_service/index.ts create mode 100644 x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx create mode 100644 x-pack/plugins/lens/public/app_plugin/types.ts create mode 100644 x-pack/plugins/lens/public/lens_attribute_service.ts diff --git a/src/plugins/dashboard/public/application/actions/library_notification_action.tsx b/src/plugins/dashboard/public/application/actions/library_notification_action.tsx index 974b55275ccc1d..bff0236c802f1f 100644 --- a/src/plugins/dashboard/public/application/actions/library_notification_action.tsx +++ b/src/plugins/dashboard/public/application/actions/library_notification_action.tsx @@ -79,6 +79,7 @@ export class LibraryNotificationAction implements ActionByType { return ( + embeddable.getRoot().isContainer && embeddable.getInput()?.viewMode !== ViewMode.VIEW && isReferenceOrValueEmbeddable(embeddable) && embeddable.inputIsRefType(embeddable.getInput()) diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx index dd5eb1ee5ccaa4..e5b467a4181770 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx @@ -66,7 +66,6 @@ import { ViewMode, ContainerOutput, EmbeddableInput, - SavedObjectEmbeddableInput, } from '../../../embeddable/public'; import { NavAction, SavedDashboardPanel } from '../types'; @@ -178,7 +177,7 @@ export class DashboardAppController { chrome.docTitle.change(dash.title); } - const incomingEmbeddable = embeddable + let incomingEmbeddable = embeddable .getStateTransfer(scopedHistory()) .getIncomingEmbeddablePackage(); @@ -344,6 +343,22 @@ export class DashboardAppController { dashboardStateManager.getPanels().forEach((panel: SavedDashboardPanel) => { embeddablesMap[panel.panelIndex] = convertSavedDashboardPanelToPanelState(panel); }); + + // If the incoming embeddable state's id already exists in the embeddables map, replace the input, retaining the existing gridData for that panel. + if (incomingEmbeddable?.embeddableId && embeddablesMap[incomingEmbeddable.embeddableId]) { + const originalPanelState = embeddablesMap[incomingEmbeddable.embeddableId]; + embeddablesMap[incomingEmbeddable.embeddableId] = { + gridData: originalPanelState.gridData, + type: incomingEmbeddable.type, + explicitInput: { + ...originalPanelState.explicitInput, + ...incomingEmbeddable.input, + id: incomingEmbeddable.embeddableId, + }, + }; + incomingEmbeddable = undefined; + } + let expandedPanelId; if (dashboardContainer && !isErrorEmbeddable(dashboardContainer)) { expandedPanelId = dashboardContainer.getInput().expandedPanelId; @@ -482,32 +497,16 @@ export class DashboardAppController { refreshDashboardContainer(); }); - if (incomingEmbeddable) { - if ('id' in incomingEmbeddable) { - container.addOrUpdateEmbeddable( - incomingEmbeddable.type, - { - savedObjectId: incomingEmbeddable.id, - } - ); - } else if ('input' in incomingEmbeddable) { - const input = incomingEmbeddable.input; - // @ts-expect-error - delete input.id; - const explicitInput = { - savedVis: input, - }; - const embeddableId = - 'embeddableId' in incomingEmbeddable - ? incomingEmbeddable.embeddableId - : undefined; - container.addOrUpdateEmbeddable( - incomingEmbeddable.type, - // This ugly solution is temporary - https://github.com/elastic/kibana/pull/70272 fixes this whole section - (explicitInput as unknown) as EmbeddableInput, - embeddableId - ); - } + // If the incomingEmbeddable does not yet exist in the panels listing, create a new panel using the container's addEmbeddable method. + if ( + incomingEmbeddable && + (!incomingEmbeddable.embeddableId || + !container.getInput().panels[incomingEmbeddable.embeddableId]) + ) { + container.addNewEmbeddable( + incomingEmbeddable.type, + incomingEmbeddable.input + ); } } diff --git a/src/plugins/dashboard/public/attribute_service/attribute_service_mock.tsx b/src/plugins/dashboard/public/attribute_service/attribute_service.mock.tsx similarity index 100% rename from src/plugins/dashboard/public/attribute_service/attribute_service_mock.tsx rename to src/plugins/dashboard/public/attribute_service/attribute_service.mock.tsx diff --git a/src/plugins/dashboard/public/attribute_service/attribute_service.test.ts b/src/plugins/dashboard/public/attribute_service/attribute_service.test.ts index 06f380ca3862b6..ae8f034aec687c 100644 --- a/src/plugins/dashboard/public/attribute_service/attribute_service.test.ts +++ b/src/plugins/dashboard/public/attribute_service/attribute_service.test.ts @@ -18,7 +18,7 @@ */ import { ATTRIBUTE_SERVICE_KEY } from './attribute_service'; -import { mockAttributeService } from './attribute_service_mock'; +import { mockAttributeService } from './attribute_service.mock'; import { coreMock } from '../../../../core/public/mocks'; interface TestAttributes { diff --git a/src/plugins/dashboard/public/attribute_service/attribute_service.tsx b/src/plugins/dashboard/public/attribute_service/attribute_service.tsx index 84df05154fb630..7499a6fced72ac 100644 --- a/src/plugins/dashboard/public/attribute_service/attribute_service.tsx +++ b/src/plugins/dashboard/public/attribute_service/attribute_service.tsx @@ -156,12 +156,8 @@ export class AttributeService< }; public getExplicitInputFromEmbeddable(embeddable: IEmbeddable): ValType | RefType { - return embeddable.getRoot() && - (embeddable.getRoot() as Container).getInput().panels[embeddable.id].explicitInput - ? ((embeddable.getRoot() as Container).getInput().panels[embeddable.id].explicitInput as - | ValType - | RefType) - : (embeddable.getInput() as ValType | RefType); + return ((embeddable.getRoot() as Container).getInput()?.panels?.[embeddable.id] + ?.explicitInput ?? embeddable.getInput()) as ValType | RefType; } getInputAsValueType = async (input: ValType | RefType): Promise => { @@ -204,7 +200,14 @@ export class AttributeService< const newAttributes = { ...input[ATTRIBUTE_SERVICE_KEY] }; newAttributes.title = props.newTitle; const wrappedInput = (await this.wrapAttributes(newAttributes, true)) as RefType; - resolve(wrappedInput); + + // Remove unneeded attributes from the original input. + delete (input as { [ATTRIBUTE_SERVICE_KEY]?: SavedObjectAttributes })[ + ATTRIBUTE_SERVICE_KEY + ]; + + // Combine input and wrapped input to preserve any passed in explicit Input. + resolve({ ...input, ...wrappedInput }); return { id: wrappedInput.savedObjectId }; } catch (error) { reject(error); diff --git a/src/plugins/dashboard/public/attribute_service/index.ts b/src/plugins/dashboard/public/attribute_service/index.ts new file mode 100644 index 00000000000000..84d4c8a13c31ed --- /dev/null +++ b/src/plugins/dashboard/public/attribute_service/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { AttributeService, ATTRIBUTE_SERVICE_KEY } from './attribute_service'; diff --git a/src/plugins/dashboard/public/index.ts b/src/plugins/dashboard/public/index.ts index e22d1f038a4560..315afd61c7c44e 100644 --- a/src/plugins/dashboard/public/index.ts +++ b/src/plugins/dashboard/public/index.ts @@ -31,7 +31,7 @@ export { } from './application'; export { DashboardConstants, createDashboardEditUrl } from './dashboard_constants'; -export { DashboardStart, DashboardUrlGenerator } from './plugin'; +export { DashboardStart, DashboardUrlGenerator, DashboardFeatureFlagConfig } from './plugin'; export { DASHBOARD_APP_URL_GENERATOR, createDashboardUrlGenerator, @@ -40,7 +40,7 @@ export { export { addEmbeddableToDashboardUrl } from './url_utils/url_helper'; export { SavedObjectDashboard } from './saved_dashboards'; export { SavedDashboardPanel } from './types'; -export { AttributeService, ATTRIBUTE_SERVICE_KEY } from './attribute_service/attribute_service'; +export { AttributeService, ATTRIBUTE_SERVICE_KEY } from './attribute_service'; export function plugin(initializerContext: PluginInitializerContext) { return new DashboardPlugin(initializerContext); diff --git a/src/plugins/dashboard/public/mocks.tsx b/src/plugins/dashboard/public/mocks.tsx index ba30d72594f2a1..07f29eca530425 100644 --- a/src/plugins/dashboard/public/mocks.tsx +++ b/src/plugins/dashboard/public/mocks.tsx @@ -20,6 +20,7 @@ import { DashboardStart } from './plugin'; export type Start = jest.Mocked; +export { mockAttributeService } from './attribute_service/attribute_service.mock'; const createStartContract = (): DashboardStart => { // @ts-ignore diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 5a45229a58a7d4..eadb3cd207e4dc 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -117,7 +117,7 @@ declare module '../../share/public' { export type DashboardUrlGenerator = UrlGeneratorContract; -interface DashboardFeatureFlagConfig { +export interface DashboardFeatureFlagConfig { allowByValueEmbeddables: boolean; } diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx index 8c3d7ab9c30d09..ba24913c6d1c02 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx @@ -18,7 +18,7 @@ */ import { EditPanelAction } from './edit_panel_action'; -import { Embeddable, EmbeddableInput } from '../embeddables'; +import { Embeddable, EmbeddableInput, SavedObjectEmbeddableInput } from '../embeddables'; import { ViewMode } from '../types'; import { ContactCardEmbeddable } from '../test_samples'; import { embeddablePluginMock } from '../../mocks'; @@ -53,20 +53,50 @@ test('is compatible when edit url is available, in edit mode and editable', asyn ).toBe(true); }); -test('redirects to app using state transfer', async () => { +test('redirects to app using state transfer with by value mode', async () => { applicationMock.currentAppId$ = of('superCoolCurrentApp'); const action = new EditPanelAction(getFactory, applicationMock, stateTransferMock); - const input = { id: '123', viewMode: ViewMode.EDIT }; - const embeddable = new EditableEmbeddable(input, true); + const embeddable = new EditableEmbeddable( + ({ + id: '123', + viewMode: ViewMode.EDIT, + coolInput1: 1, + coolInput2: 2, + } as unknown) as EmbeddableInput, + true + ); + embeddable.getOutput = jest.fn(() => ({ editApp: 'ultraVisualize', editPath: '/123' })); + await action.execute({ embeddable }); + expect(stateTransferMock.navigateToEditor).toHaveBeenCalledWith('ultraVisualize', { + path: '/123', + state: { + originatingApp: 'superCoolCurrentApp', + embeddableId: '123', + valueInput: { + id: '123', + viewMode: ViewMode.EDIT, + coolInput1: 1, + coolInput2: 2, + }, + }, + }); +}); + +test('redirects to app using state transfer without by value mode', async () => { + applicationMock.currentAppId$ = of('superCoolCurrentApp'); + const action = new EditPanelAction(getFactory, applicationMock, stateTransferMock); + const embeddable = new EditableEmbeddable( + { id: '123', viewMode: ViewMode.EDIT, savedObjectId: '1234' } as SavedObjectEmbeddableInput, + true + ); embeddable.getOutput = jest.fn(() => ({ editApp: 'ultraVisualize', editPath: '/123' })); await action.execute({ embeddable }); expect(stateTransferMock.navigateToEditor).toHaveBeenCalledWith('ultraVisualize', { path: '/123', state: { originatingApp: 'superCoolCurrentApp', - byValueMode: true, embeddableId: '123', - valueInput: input, + valueInput: undefined, }, }); }); diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts index 8d12ddd0299e7a..cbc28713197ba1 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts @@ -29,6 +29,8 @@ import { EmbeddableEditorState, EmbeddableStateTransfer, SavedObjectEmbeddableInput, + EmbeddableInput, + Container, } from '../..'; export const ACTION_EDIT_PANEL = 'editPanel'; @@ -118,8 +120,7 @@ export class EditPanelAction implements Action { const byValueMode = !(embeddable.getInput() as SavedObjectEmbeddableInput).savedObjectId; const state: EmbeddableEditorState = { originatingApp: this.currentAppId, - byValueMode, - valueInput: byValueMode ? embeddable.getInput() : undefined, + valueInput: byValueMode ? this.getExplicitInput({ embeddable }) : undefined, embeddableId: embeddable.id, }; return { app, path, state }; @@ -132,4 +133,11 @@ export class EditPanelAction implements Action { const editUrl = embeddable ? embeddable.getOutput().editUrl : undefined; return editUrl ? editUrl : ''; } + + private getExplicitInput({ embeddable }: ActionContext): EmbeddableInput { + return ( + (embeddable.getRoot() as Container)?.getInput()?.panels?.[embeddable.id]?.explicitInput ?? + embeddable.getInput() + ); + } } diff --git a/src/plugins/embeddable/public/lib/containers/container.ts b/src/plugins/embeddable/public/lib/containers/container.ts index 38975cc220bc26..9f701f021162a1 100644 --- a/src/plugins/embeddable/public/lib/containers/container.ts +++ b/src/plugins/embeddable/public/lib/containers/container.ts @@ -199,8 +199,8 @@ export abstract class Container< return { type: factory.type, explicitInput: { - id: embeddableId, ...explicitInput, + id: embeddableId, } as TEmbeddableInput, }; } diff --git a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts index ef79b18acd4f5b..4155cb4d3b60c7 100644 --- a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts +++ b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts @@ -85,10 +85,10 @@ describe('embeddable state transfer', () => { it('can send an outgoing embeddable package state', async () => { await stateTransfer.navigateToWithEmbeddablePackage(destinationApp, { - state: { type: 'coolestType', id: '150' }, + state: { type: 'coolestType', input: { savedObjectId: '150' } }, }); expect(application.navigateToApp).toHaveBeenCalledWith('superUltraVisualize', { - state: { type: 'coolestType', id: '150' }, + state: { type: 'coolestType', input: { savedObjectId: '150' } }, }); }); @@ -96,12 +96,16 @@ describe('embeddable state transfer', () => { const historyMock = mockHistoryState({ kibanaIsNowForSports: 'extremeSportsKibana' }); stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); await stateTransfer.navigateToWithEmbeddablePackage(destinationApp, { - state: { type: 'coolestType', id: '150' }, + state: { type: 'coolestType', input: { savedObjectId: '150' } }, appendToExistingState: true, }); expect(application.navigateToApp).toHaveBeenCalledWith('superUltraVisualize', { path: undefined, - state: { kibanaIsNowForSports: 'extremeSportsKibana', type: 'coolestType', id: '150' }, + state: { + kibanaIsNowForSports: 'extremeSportsKibana', + type: 'coolestType', + input: { savedObjectId: '150' }, + }, }); }); @@ -120,10 +124,13 @@ describe('embeddable state transfer', () => { }); it('can fetch an incoming embeddable package state', async () => { - const historyMock = mockHistoryState({ type: 'skisEmbeddable', id: '123' }); + const historyMock = mockHistoryState({ + type: 'skisEmbeddable', + input: { savedObjectId: '123' }, + }); stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); const fetchedState = stateTransfer.getIncomingEmbeddablePackage(); - expect(fetchedState).toEqual({ type: 'skisEmbeddable', id: '123' }); + expect(fetchedState).toEqual({ type: 'skisEmbeddable', input: { savedObjectId: '123' } }); }); it('returns undefined when embeddable package is not in the right shape', async () => { @@ -136,12 +143,12 @@ describe('embeddable state transfer', () => { it('removes all keys in the keysToRemoveAfterFetch array', async () => { const historyMock = mockHistoryState({ type: 'skisEmbeddable', - id: '123', + input: { savedObjectId: '123' }, test1: 'test1', test2: 'test2', }); stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); - stateTransfer.getIncomingEmbeddablePackage({ keysToRemoveAfterFetch: ['type', 'id'] }); + stateTransfer.getIncomingEmbeddablePackage({ keysToRemoveAfterFetch: ['type', 'input'] }); expect(historyMock.replace).toHaveBeenCalledWith( expect.objectContaining({ state: { test1: 'test1', test2: 'test2' } }) ); @@ -150,7 +157,7 @@ describe('embeddable state transfer', () => { it('leaves state as is when no keysToRemove are supplied', async () => { const historyMock = mockHistoryState({ type: 'skisEmbeddable', - id: '123', + input: { savedObjectId: '123' }, test1: 'test1', test2: 'test2', }); @@ -158,7 +165,7 @@ describe('embeddable state transfer', () => { stateTransfer.getIncomingEmbeddablePackage(); expect(historyMock.location.state).toEqual({ type: 'skisEmbeddable', - id: '123', + input: { savedObjectId: '123' }, test1: 'test1', test2: 'test2', }); diff --git a/src/plugins/embeddable/public/lib/state_transfer/types.ts b/src/plugins/embeddable/public/lib/state_transfer/types.ts index 3f3456d914728b..d8b4f4801bba3c 100644 --- a/src/plugins/embeddable/public/lib/state_transfer/types.ts +++ b/src/plugins/embeddable/public/lib/state_transfer/types.ts @@ -17,17 +17,17 @@ * under the License. */ -import { EmbeddableInput } from '..'; +import { Optional } from '@kbn/utility-types'; +import { EmbeddableInput, SavedObjectEmbeddableInput } from '..'; /** - * Represents a state package that contains the last active app id. + * A state package that contains information an editor will need to create or edit an embeddable then redirect back. * @public */ export interface EmbeddableEditorState { originatingApp: string; - byValueMode?: boolean; - valueInput?: EmbeddableInput; embeddableId?: string; + valueInput?: EmbeddableInput; } export function isEmbeddableEditorState(state: unknown): state is EmbeddableEditorState { @@ -35,32 +35,18 @@ export function isEmbeddableEditorState(state: unknown): state is EmbeddableEdit } /** - * Represents a state package that contains all fields necessary to create an embeddable by reference in a container. + * A state package that contains all fields necessary to create or update an embeddable by reference or by value in a container. * @public */ -export interface EmbeddablePackageByReferenceState { +export interface EmbeddablePackageState { type: string; - id: string; -} - -/** - * Represents a state package that contains all fields necessary to create an embeddable by value in a container. - * @public - */ -export interface EmbeddablePackageByValueState { - type: string; - input: EmbeddableInput; + input: Optional | Optional; embeddableId?: string; } -export type EmbeddablePackageState = - | EmbeddablePackageByReferenceState - | EmbeddablePackageByValueState; - export function isEmbeddablePackageState(state: unknown): state is EmbeddablePackageState { return ( - (ensureFieldOfTypeExists('type', state, 'string') && - ensureFieldOfTypeExists('id', state, 'string')) || + ensureFieldOfTypeExists('type', state, 'string') && ensureFieldOfTypeExists('input', state, 'object') ); } diff --git a/src/plugins/saved_objects/public/save_modal/saved_object_save_modal_origin.tsx b/src/plugins/saved_objects/public/save_modal/saved_object_save_modal_origin.tsx index ce08151d37c2c3..dfc0c4049774d9 100644 --- a/src/plugins/saved_objects/public/save_modal/saved_object_save_modal_origin.tsx +++ b/src/plugins/saved_objects/public/save_modal/saved_object_save_modal_origin.tsx @@ -33,6 +33,8 @@ interface SaveModalDocumentInfo { interface OriginSaveModalProps { originatingApp?: string; getAppNameFromId?: (appId: string) => string | undefined; + originatingAppName?: string; + returnToOriginSwitchLabel?: string; documentInfo: SaveModalDocumentInfo; objectType: string; onClose: () => void; @@ -73,11 +75,13 @@ export function SavedObjectSaveModalOrigin(props: OriginSaveModalProps) { setReturnToOriginMode(event.target.checked); }} label={ - + props.returnToOriginSwitchLabel ?? ( + + ) } /> diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx index 1d01900ceffc28..a9bf6bd171f155 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx @@ -174,7 +174,9 @@ class NewVisModal extends React.Component void; originatingApp?: string; outsideVisualizeApp?: boolean; + createByValue?: boolean; } /** diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx index 65c9a5410d2269..12720f3f22e7cf 100644 --- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx @@ -21,8 +21,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { TopNavMenuData } from 'src/plugins/navigation/public'; -import uuid from 'uuid'; -import { VISUALIZE_EMBEDDABLE_TYPE } from '../../../../visualizations/public'; +import { VISUALIZE_EMBEDDABLE_TYPE, VisualizeInput } from '../../../../visualizations/public'; import { showSaveModal, SavedObjectSaveModalOrigin, @@ -122,7 +121,7 @@ export const getTopNavConfig = ( if (newlyCreated && stateTransfer) { stateTransfer.navigateToWithEmbeddablePackage(originatingApp, { - state: { id, type: VISUALIZE_EMBEDDABLE_TYPE }, + state: { type: VISUALIZE_EMBEDDABLE_TYPE, input: { savedObjectId: id } }, }); } else { application.navigateToApp(originatingApp); @@ -167,15 +166,11 @@ export const getTopNavConfig = ( } const state = { input: { - ...vis.serialize(), - id: embeddableId ? embeddableId : uuid.v4(), - }, + savedVis: vis.serialize(), + } as VisualizeInput, + embeddableId, type: VISUALIZE_EMBEDDABLE_TYPE, - embeddableId: '', }; - if (embeddableId) { - state.embeddableId = embeddableId; - } stateTransfer.navigateToWithEmbeddablePackage(originatingApp, { state }); }; @@ -283,6 +278,7 @@ export const getTopNavConfig = ( } return response; }; + const saveModal = ( { - let frame: jest.Mocked; let core: ReturnType; - let instance: ReactWrapper; - - function makeDefaultArgs(): jest.Mocked<{ - editorFrame: EditorFrameInstance; - data: typeof dataStartMock; - navigation: typeof navigationStartMock; - core: typeof core; - storage: Storage; - docId?: string; - docStorage: SavedObjectStore; - redirectTo: (id?: string, returnToOrigin?: boolean, newlyCreated?: boolean) => void; - originatingApp: string | undefined; - onAppLeave: AppMountParameters['onAppLeave']; - setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; - history: History; - getAppNameFromId?: (appId: string) => string | undefined; - }> { - return ({ - navigation: navigationStartMock, + let defaultDoc: Document; + let defaultSavedObjectId: string; + + const navMenuItems = { + expectedSaveButton: { emphasize: true, testId: 'lnsApp_saveButton' }, + expectedSaveAsButton: { emphasize: false, testId: 'lnsApp_saveButton' }, + expectedSaveAndReturnButton: { emphasize: true, testId: 'lnsApp_saveAndReturnButton' }, + }; + + function makeAttributeService(): LensAttributeService { + const attributeServiceMock = mockAttributeService< + LensSavedObjectAttributes, + LensByValueInput, + LensByReferenceInput + >( + DOC_TYPE, + { + customSaveMethod: jest.fn(), + customUnwrapMethod: jest.fn(), + }, + core + ); + attributeServiceMock.unwrapAttributes = jest.fn().mockResolvedValue(defaultDoc); + attributeServiceMock.wrapAttributes = jest + .fn() + .mockResolvedValue({ savedObjectId: defaultSavedObjectId }); + return attributeServiceMock; + } + + function makeDefaultProps(): jest.Mocked { + return { editorFrame: createMockFrame(), - core: { - ...core, - application: { - ...core.application, - capabilities: { - ...core.application.capabilities, - visualize: { save: true, saveQuery: true, show: true }, - }, + history: createMemoryHistory(), + redirectTo: jest.fn(), + redirectToOrigin: jest.fn(), + onAppLeave: jest.fn(), + setHeaderActionMenu: jest.fn(), + }; + } + + function makeDefaultServices(): jest.Mocked { + return { + http: core.http, + chrome: core.chrome, + overlays: core.overlays, + uiSettings: core.uiSettings, + navigation: navigationStartMock, + notifications: core.notifications, + attributeService: makeAttributeService(), + savedObjectsClient: core.savedObjects.client, + dashboardFeatureFlag: { allowByValueEmbeddables: false }, + getOriginatingAppName: jest.fn(() => 'defaultOriginatingApp'), + application: { + ...core.application, + capabilities: { + ...core.application.capabilities, + visualize: { save: true, saveQuery: true, show: true }, }, + getUrlForApp: jest.fn((appId: string) => `/testbasepath/app/${appId}#/`), }, - data: { + data: ({ query: { filterManager: createMockFilterManager(), timefilter: { @@ -166,38 +202,52 @@ describe('Lens App', () => { return new Promise((resolve) => resolve({ id })); }), }, - }, + } as unknown) as DataPublicPluginStart, storage: { get: jest.fn(), + set: jest.fn(), + remove: jest.fn(), + clear: jest.fn(), }, - docStorage: { - load: jest.fn(), - save: jest.fn(), - }, - redirectTo: jest.fn((id?: string, returnToOrigin?: boolean, newlyCreated?: boolean) => {}), - onAppLeave: jest.fn(), - setHeaderActionMenu: jest.fn(), - history: createMemoryHistory(), - } as unknown) as jest.Mocked<{ - navigation: typeof navigationStartMock; - editorFrame: EditorFrameInstance; - data: typeof dataStartMock; - core: typeof core; - storage: Storage; - docId?: string; - docStorage: SavedObjectStore; - redirectTo: (id?: string, returnToOrigin?: boolean, newlyCreated?: boolean) => void; - originatingApp: string | undefined; - onAppLeave: AppMountParameters['onAppLeave']; - setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; - history: History; - getAppNameFromId?: (appId: string) => string | undefined; - }>; + }; + } + + function mountWith({ + props: incomingProps, + services: incomingServices, + }: { + props?: jest.Mocked; + services?: jest.Mocked; + }) { + const props = incomingProps ?? makeDefaultProps(); + const services = incomingServices ?? makeDefaultServices(); + const wrappingComponent: React.FC<{ + children: React.ReactNode; + }> = ({ children }) => { + return ( + + {children} + + ); + }; + const frame = props.editorFrame as ReturnType; + const component = mount(, { wrappingComponent }); + return { component, frame, props, services }; } beforeEach(() => { - frame = createMockFrame(); core = coreMock.createStart({ basePath: '/testbasepath' }); + defaultSavedObjectId = '1234'; + defaultDoc = ({ + savedObjectId: defaultSavedObjectId, + title: 'An extremely cool default document!', + expression: 'definitely a valid expression', + state: { + query: 'kuery', + filters: [{ query: { match_phrase: { src: 'test' } } }], + }, + references: [{ type: 'index-pattern', id: '1', name: 'index-pattern-0' }], + } as unknown) as Document; core.uiSettings.get.mockImplementation( jest.fn((type) => { @@ -215,10 +265,7 @@ describe('Lens App', () => { }); it('renders the editor frame', () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - - mount(); + const { frame } = mountWith({}); expect(frame.mount.mock.calls).toMatchInlineSnapshot(` Array [ @@ -248,23 +295,22 @@ describe('Lens App', () => { }); it('clears app filters on load', () => { - const defaultArgs = makeDefaultArgs(); - mount(); - - expect(defaultArgs.data.query.filterManager.setAppFilters).toHaveBeenCalledWith([]); + const { services } = mountWith({}); + expect(services.data.query.filterManager.setAppFilters).toHaveBeenCalledWith([]); }); it('passes global filters to frame', async () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; + const services = makeDefaultServices(); const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; const pinnedField = ({ name: 'pinnedField' } as unknown) as IFieldType; const pinnedFilter = esFilters.buildExistsFilter(pinnedField, indexPattern); - args.data.query.filterManager.getFilters = jest.fn().mockImplementation(() => { + services.data.query.filterManager.getFilters = jest.fn().mockImplementation(() => { return [pinnedFilter]; }); - const component = mount(); + const { component, frame } = mountWith({ services }); + component.update(); + expect(frame.mount).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ @@ -275,103 +321,81 @@ describe('Lens App', () => { ); }); - it('sets breadcrumbs when the document title changes', async () => { - const defaultArgs = makeDefaultArgs(); - instance = mount(); - - expect(core.chrome.setBreadcrumbs).toHaveBeenCalledWith([ - { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, - { text: 'Create' }, - ]); + it('displays errors from the frame in a toast', () => { + const { component, frame, services } = mountWith({}); + const onError = frame.mount.mock.calls[0][1].onError; + onError({ message: 'error' }); + component.update(); + expect(services.notifications.toasts.addDanger).toHaveBeenCalled(); + }); - (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', + describe('breadcrumbs', () => { + const breadcrumbDocSavedObjectId = defaultSavedObjectId; + const breadcrumbDoc = ({ + savedObjectId: breadcrumbDocSavedObjectId, title: 'Daaaaaaadaumching!', state: { query: 'fake query', filters: [], }, references: [], - }); - await act(async () => { - instance.setProps({ docId: '1234' }); - }); + } as unknown) as Document; - expect(defaultArgs.core.chrome.setBreadcrumbs).toHaveBeenCalledWith([ - { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, - { text: 'Daaaaaaadaumching!' }, - ]); - }); + it('sets breadcrumbs when the document title changes', async () => { + const { component, services } = mountWith({}); - it('adds to the recently viewed list on load', async () => { - const defaultArgs = makeDefaultArgs(); - instance = mount(); + expect(services.chrome.setBreadcrumbs).toHaveBeenCalledWith([ + { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, + { text: 'Create' }, + ]); - (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', - title: 'Daaaaaaadaumching!', - state: { - query: 'fake query', - filters: [], - }, - references: [], - }); - await act(async () => { - instance.setProps({ docId: '1234' }); + services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue(breadcrumbDoc); + await act(async () => { + component.setProps({ initialInput: { savedObjectId: breadcrumbDocSavedObjectId } }); + }); + + expect(services.chrome.setBreadcrumbs).toHaveBeenCalledWith([ + { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, + { text: 'Daaaaaaadaumching!' }, + ]); }); - expect(defaultArgs.core.chrome.recentlyAccessed.add).toHaveBeenCalledWith( - '/app/lens#/edit/1234', - 'Daaaaaaadaumching!', - '1234' - ); - }); - it('sets originatingApp breadcrumb when the document title changes', async () => { - const defaultArgs = makeDefaultArgs(); - defaultArgs.originatingApp = 'ultraCoolDashboard'; - defaultArgs.getAppNameFromId = () => 'The Coolest Container Ever Made'; - instance = mount(); + it('sets originatingApp breadcrumb when the document title changes', async () => { + const props = makeDefaultProps(); + const services = makeDefaultServices(); + props.incomingState = { originatingApp: 'coolContainer' }; + services.getOriginatingAppName = jest.fn(() => 'The Coolest Container Ever Made'); + const { component } = mountWith({ props, services }); + + expect(services.chrome.setBreadcrumbs).toHaveBeenCalledWith([ + { text: 'The Coolest Container Ever Made', onClick: expect.anything() }, + { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, + { text: 'Create' }, + ]); - expect(core.chrome.setBreadcrumbs).toHaveBeenCalledWith([ - { text: 'The Coolest Container Ever Made', onClick: expect.anything() }, - { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, - { text: 'Create' }, - ]); + services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue(breadcrumbDoc); + await act(async () => { + component.setProps({ initialInput: { savedObjectId: breadcrumbDocSavedObjectId } }); + }); - (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', - title: 'Daaaaaaadaumching!', - state: { - query: 'fake query', - filters: [], - }, - references: [], - }); - await act(async () => { - instance.setProps({ docId: '1234' }); + expect(services.chrome.setBreadcrumbs).toHaveBeenCalledWith([ + { text: 'The Coolest Container Ever Made', onClick: expect.anything() }, + { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, + { text: 'Daaaaaaadaumching!' }, + ]); }); - - expect(defaultArgs.core.chrome.setBreadcrumbs).toHaveBeenCalledWith([ - { text: 'The Coolest Container Ever Made', onClick: expect.anything() }, - { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, - { text: 'Daaaaaaadaumching!' }, - ]); }); describe('persistence', () => { - it('does not load a document if there is no document id', () => { - const args = makeDefaultArgs(); - - mount(); - - expect(args.docStorage.load).not.toHaveBeenCalled(); + it('does not load a document if there is no initial input', () => { + const { services } = mountWith({}); + expect(services.attributeService.unwrapAttributes).not.toHaveBeenCalled(); }); - it('loads a document and uses query and filters if there is a document id', async () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - (args.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', + it('loads a document and uses query and filters if initial input is provided', async () => { + const { component, frame, services } = mountWith({}); + services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue({ + savedObjectId: defaultSavedObjectId, state: { query: 'fake query', filters: [{ query: { match_phrase: { src: 'test' } } }], @@ -379,15 +403,15 @@ describe('Lens App', () => { references: [{ type: 'index-pattern', id: '1', name: 'index-pattern-0' }], }); - instance = mount(); - await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - expect(args.docStorage.load).toHaveBeenCalledWith('1234'); - expect(args.data.indexPatterns.get).toHaveBeenCalledWith('1'); - expect(args.data.query.filterManager.setAppFilters).toHaveBeenCalledWith([ + expect(services.attributeService.unwrapAttributes).toHaveBeenCalledWith({ + savedObjectId: defaultSavedObjectId, + }); + expect(services.data.indexPatterns.get).toHaveBeenCalledWith('1'); + expect(services.data.query.filterManager.setAppFilters).toHaveBeenCalledWith([ { query: { match_phrase: { src: 'test' } } }, ]); expect(TopNavMenu).toHaveBeenCalledWith( @@ -401,7 +425,7 @@ describe('Lens App', () => { expect.any(Element), expect.objectContaining({ doc: expect.objectContaining({ - id: '1234', + savedObjectId: defaultSavedObjectId, state: expect.objectContaining({ query: 'fake query', filters: [{ query: { match_phrase: { src: 'test' } } }], @@ -412,65 +436,59 @@ describe('Lens App', () => { }); it('does not load documents on sequential renders unless the id changes', async () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - (args.docStorage.load as jest.Mock).mockResolvedValue({ id: '1234' }); + const { services, component } = mountWith({}); - instance = mount(); await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - - expect(args.docStorage.load).toHaveBeenCalledTimes(1); + expect(services.attributeService.unwrapAttributes).toHaveBeenCalledTimes(1); await act(async () => { - instance.setProps({ docId: '9876' }); + component.setProps({ initialInput: { savedObjectId: '5678' } }); }); - expect(args.docStorage.load).toHaveBeenCalledTimes(2); + expect(services.attributeService.unwrapAttributes).toHaveBeenCalledTimes(2); }); it('handles document load errors', async () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - (args.docStorage.load as jest.Mock).mockRejectedValue('failed to load'); - - instance = mount(); + const services = makeDefaultServices(); + services.attributeService.unwrapAttributes = jest.fn().mockRejectedValue('failed to load'); + const { component, props } = mountWith({ services }); await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - expect(args.docStorage.load).toHaveBeenCalledWith('1234'); - expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled(); - expect(args.redirectTo).toHaveBeenCalled(); + expect(services.attributeService.unwrapAttributes).toHaveBeenCalledWith({ + savedObjectId: defaultSavedObjectId, + }); + expect(services.notifications.toasts.addDanger).toHaveBeenCalled(); + expect(props.redirectTo).toHaveBeenCalled(); }); - describe('save button', () => { + it('adds to the recently accessed list on load', async () => { + const { component, services } = mountWith({}); + + await act(async () => { + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); + }); + expect(services.chrome.recentlyAccessed.add).toHaveBeenCalledWith( + '/app/lens#/edit/1234', + 'An extremely cool default document!', + '1234' + ); + }); + + describe('save buttons', () => { interface SaveProps { newCopyOnSave: boolean; returnToOrigin?: boolean; newTitle: string; } - let defaultArgs: ReturnType; - - beforeEach(() => { - defaultArgs = makeDefaultArgs(); - (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', - title: 'My cool doc', - expression: 'valid expression', - state: { - query: 'kuery', - }, - } as jest.ResolvedValue); - }); - function getButton(inst: ReactWrapper): TopNavMenuData { return (inst .find('[data-test-subj="lnsApp_topNav"]') @@ -495,135 +513,195 @@ describe('Lens App', () => { filters: [], }, }, - initialDocId, + initialSavedObjectId, ...saveProps }: SaveProps & { lastKnownDoc?: object; - initialDocId?: string; + initialSavedObjectId?: string; }) { - const args = { - ...defaultArgs, - docId: initialDocId, + const props = { + ...makeDefaultProps(), + initialInput: initialSavedObjectId + ? { savedObjectId: initialSavedObjectId, id: '5678' } + : undefined, }; - args.editorFrame = frame; - (args.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', + + const services = makeDefaultServices(); + services.attributeService.wrapAttributes = jest + .fn() + .mockImplementation(async ({ savedObjectId }) => ({ + savedObjectId: savedObjectId || 'aaa', + })); + services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue({ + savedObjectId: initialSavedObjectId ?? 'aaa', references: [], state: { query: 'fake query', filters: [], }, - }); - (args.docStorage.save as jest.Mock).mockImplementation(async ({ id }) => ({ - id: id || 'aaa', - })); + } as jest.ResolvedValue); + let frame: jest.Mocked = {} as jest.Mocked; + let component: ReactWrapper = {} as ReactWrapper; await act(async () => { - instance = mount(); + const { frame: newFrame, component: newComponent } = mountWith({ services, props }); + frame = newFrame; + component = newComponent; }); - if (initialDocId) { - expect(args.docStorage.load).toHaveBeenCalledTimes(1); + if (initialSavedObjectId) { + expect(services.attributeService.unwrapAttributes).toHaveBeenCalledTimes(1); } else { - expect(args.docStorage.load).not.toHaveBeenCalled(); + expect(services.attributeService.unwrapAttributes).not.toHaveBeenCalled(); } const onChange = frame.mount.mock.calls[0][1].onChange; + act(() => onChange({ filterableIndexPatterns: [], - doc: { id: initialDocId, ...lastKnownDoc } as Document, + doc: { savedObjectId: initialSavedObjectId, ...lastKnownDoc } as Document, isSaveable: true, }) ); - - instance.update(); - - expect(getButton(instance).disableButton).toEqual(false); - + component.update(); + expect(getButton(component).disableButton).toEqual(false); await act(async () => { - testSave(instance, { ...saveProps }); + testSave(component, { ...saveProps }); }); - - return { args, instance }; + return { props, services, component, frame }; } it('shows a disabled save button when the user does not have permissions', async () => { - const args = defaultArgs; - args.core.application = { - ...args.core.application, + const services = makeDefaultServices(); + services.application = { + ...services.application, capabilities: { - ...args.core.application.capabilities, + ...services.application.capabilities, visualize: { save: false, saveQuery: false, show: true }, }, }; - args.editorFrame = frame; - - instance = mount(); - - expect(getButton(instance).disableButton).toEqual(true); - + const { component, frame } = mountWith({ services }); + expect(getButton(component).disableButton).toEqual(true); const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], - doc: ({ id: 'will save this' } as unknown) as Document, + doc: ({ savedObjectId: 'will save this' } as unknown) as Document, isSaveable: true, }) ); - instance.update(); - expect(getButton(instance).disableButton).toEqual(true); + component.update(); + expect(getButton(component).disableButton).toEqual(true); }); - it('shows a save button that is enabled when the frame has provided its state', async () => { - const args = defaultArgs; - args.editorFrame = frame; - - instance = mount(); - - expect(getButton(instance).disableButton).toEqual(true); - + it('shows a save button that is enabled when the frame has provided its state and does not show save and return or save as', async () => { + const { component, frame } = mountWith({}); + expect(getButton(component).disableButton).toEqual(true); const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], - doc: ({ id: 'will save this' } as unknown) as Document, + doc: ({ savedObjectId: 'will save this' } as unknown) as Document, isSaveable: true, }) ); - instance.update(); + component.update(); + expect(getButton(component).disableButton).toEqual(false); - expect(getButton(instance).disableButton).toEqual(false); + await act(async () => { + const topNavMenuConfig = component.find(TopNavMenu).prop('config'); + expect(topNavMenuConfig).not.toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveAndReturnButton) + ); + expect(topNavMenuConfig).not.toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveAsButton) + ); + expect(topNavMenuConfig).toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveButton) + ); + }); + }); + + it('Shows Save and Return and Save As buttons in create by value mode', async () => { + const props = makeDefaultProps(); + const services = makeDefaultServices(); + services.dashboardFeatureFlag = { allowByValueEmbeddables: true }; + props.incomingState = { + originatingApp: 'ultraDashboard', + valueInput: { + id: 'whatchaGonnaDoWith', + attributes: { + title: + 'whatcha gonna do with all these references? All these references in your value Input', + references: [] as SavedObjectReference[], + }, + } as LensByValueInput, + }; + + const { component } = mountWith({ props, services }); + + await act(async () => { + const topNavMenuConfig = component.find(TopNavMenu).prop('config'); + expect(topNavMenuConfig).toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveAndReturnButton) + ); + expect(topNavMenuConfig).toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveAsButton) + ); + expect(topNavMenuConfig).not.toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveButton) + ); + }); + }); + + it('Shows Save and Return and Save As buttons in edit by reference mode', async () => { + const props = makeDefaultProps(); + props.initialInput = { savedObjectId: defaultSavedObjectId, id: '5678' }; + props.incomingState = { + originatingApp: 'ultraDashboard', + }; + + const { component } = mountWith({ props }); + + await act(async () => { + const topNavMenuConfig = component.find(TopNavMenu).prop('config'); + expect(topNavMenuConfig).toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveAndReturnButton) + ); + expect(topNavMenuConfig).toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveAsButton) + ); + expect(topNavMenuConfig).not.toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveButton) + ); + }); }); it('saves new docs', async () => { - const { args, instance: inst } = await save({ - initialDocId: undefined, + const { props, services } = await save({ + initialSavedObjectId: undefined, newCopyOnSave: false, newTitle: 'hello there', }); - - expect(args.docStorage.save).toHaveBeenCalledWith( + expect(services.attributeService.wrapAttributes).toHaveBeenCalledWith( expect.objectContaining({ - id: undefined, + savedObjectId: undefined, title: 'hello there', - }) + }), + true, + undefined ); - - expect(args.redirectTo).toHaveBeenCalledWith('aaa', undefined, true); - - inst.setProps({ docId: 'aaa' }); - - expect(args.docStorage.load).not.toHaveBeenCalled(); + expect(props.redirectTo).toHaveBeenCalledWith('aaa'); }); - it('adds to the recently viewed list on save', async () => { - const { args } = await save({ - initialDocId: undefined, + it('adds to the recently accessed list on save', async () => { + const { services } = await save({ + initialSavedObjectId: undefined, newCopyOnSave: false, newTitle: 'hello there', }); - expect(args.core.chrome.recentlyAccessed.add).toHaveBeenCalledWith( + expect(services.chrome.recentlyAccessed.add).toHaveBeenCalledWith( '/app/lens#/edit/aaa', 'hello there', 'aaa' @@ -631,54 +709,53 @@ describe('Lens App', () => { }); it('saves the latest doc as a copy', async () => { - const { args, instance: inst } = await save({ - initialDocId: '1234', + const { props, services, component } = await save({ + initialSavedObjectId: defaultSavedObjectId, newCopyOnSave: true, newTitle: 'hello there', }); - - expect(args.docStorage.save).toHaveBeenCalledWith( + expect(services.attributeService.wrapAttributes).toHaveBeenCalledWith( expect.objectContaining({ - id: undefined, + savedObjectId: undefined, title: 'hello there', - }) + }), + true, + undefined ); - - expect(args.redirectTo).toHaveBeenCalledWith('aaa', undefined, true); - - inst.setProps({ docId: 'aaa' }); - - expect(args.docStorage.load).toHaveBeenCalledTimes(1); + expect(props.redirectTo).toHaveBeenCalledWith('aaa'); + await act(async () => { + component.setProps({ initialInput: { savedObjectId: 'aaa' } }); + }); + expect(services.attributeService.wrapAttributes).toHaveBeenCalledTimes(1); }); it('saves existing docs', async () => { - const { args, instance: inst } = await save({ - initialDocId: '1234', + const { props, services, component } = await save({ + initialSavedObjectId: defaultSavedObjectId, newCopyOnSave: false, newTitle: 'hello there', }); - - expect(args.docStorage.save).toHaveBeenCalledWith( + expect(services.attributeService.wrapAttributes).toHaveBeenCalledWith( expect.objectContaining({ - id: '1234', + savedObjectId: defaultSavedObjectId, title: 'hello there', - }) + }), + true, + { id: '5678', savedObjectId: defaultSavedObjectId } ); - - expect(args.redirectTo).not.toHaveBeenCalled(); - - inst.setProps({ docId: '1234' }); - - expect(args.docStorage.load).toHaveBeenCalledTimes(1); + expect(props.redirectTo).not.toHaveBeenCalled(); + await act(async () => { + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); + }); + expect(services.attributeService.unwrapAttributes).toHaveBeenCalledTimes(1); }); it('handles save failure by showing a warning, but still allows another save', async () => { - const args = defaultArgs; - args.editorFrame = frame; - (args.docStorage.save as jest.Mock).mockRejectedValue({ message: 'failed' }); - - instance = mount(); - + const services = makeDefaultServices(); + services.attributeService.wrapAttributes = jest + .fn() + .mockRejectedValue({ message: 'failed' }); + const { component, props, frame } = mountWith({ services }); const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ @@ -687,51 +764,48 @@ describe('Lens App', () => { isSaveable: true, }) ); - - instance.update(); + component.update(); await act(async () => { - testSave(instance, { newCopyOnSave: false, newTitle: 'hello there' }); + testSave(component, { newCopyOnSave: false, newTitle: 'hello there' }); }); - - expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled(); - expect(args.redirectTo).not.toHaveBeenCalled(); - - expect(getButton(instance).disableButton).toEqual(false); + expect(services.notifications.toasts.addDanger).toHaveBeenCalled(); + expect(props.redirectTo).not.toHaveBeenCalled(); + expect(getButton(component).disableButton).toEqual(false); }); it('saves new doc and redirects to originating app', async () => { - const { args } = await save({ - initialDocId: undefined, + const { props, services } = await save({ + initialSavedObjectId: undefined, returnToOrigin: true, newCopyOnSave: false, newTitle: 'hello there', }); - - expect(args.docStorage.save).toHaveBeenCalledWith( + expect(services.attributeService.wrapAttributes).toHaveBeenCalledWith( expect.objectContaining({ - id: undefined, + savedObjectId: undefined, title: 'hello there', - }) + }), + true, + undefined ); - - expect(args.redirectTo).toHaveBeenCalledWith('aaa', true, true); + expect(props.redirectToOrigin).toHaveBeenCalledWith({ + input: { savedObjectId: 'aaa' }, + isCopied: false, + }); }); it('saves app filters and does not save pinned filters', async () => { const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; const field = ({ name: 'myfield' } as unknown) as IFieldType; const pinnedField = ({ name: 'pinnedField' } as unknown) as IFieldType; - const unpinned = esFilters.buildExistsFilter(field, indexPattern); const pinned = esFilters.buildExistsFilter(pinnedField, indexPattern); - await act(async () => { FilterManager.setFiltersStore([pinned], esFilters.FilterStateStore.GLOBAL_STATE); }); - - const { args } = await save({ - initialDocId: '1234', + const { services } = await save({ + initialSavedObjectId: defaultSavedObjectId, newCopyOnSave: false, newTitle: 'hello there2', lastKnownDoc: { @@ -741,42 +815,42 @@ describe('Lens App', () => { }, }, }); - - expect(args.docStorage.save).toHaveBeenCalledWith({ - id: '1234', - title: 'hello there2', - expression: 'kibana 3', - state: { - filters: [unpinned], + expect(services.attributeService.wrapAttributes).toHaveBeenCalledWith( + { + savedObjectId: defaultSavedObjectId, + title: 'hello there2', + expression: 'kibana 3', + state: { + filters: [unpinned], + }, }, - }); + true, + { id: '5678', savedObjectId: defaultSavedObjectId } + ); }); it('checks for duplicate title before saving', async () => { - const args = defaultArgs; - args.editorFrame = frame; - (args.docStorage.save as jest.Mock).mockReturnValue(Promise.resolve({ id: '123' })); - - instance = mount(); - + const services = makeDefaultServices(); + services.attributeService.wrapAttributes = jest + .fn() + .mockReturnValue(Promise.resolve({ savedObjectId: '123' })); + const { component, frame } = mountWith({ services }); const onChange = frame.mount.mock.calls[0][1].onChange; await act(async () => onChange({ filterableIndexPatterns: [], - doc: ({ id: '123' } as unknown) as Document, + doc: ({ savedObjectId: '123' } as unknown) as Document, isSaveable: true, }) ); - instance.update(); + component.update(); await act(async () => { - getButton(instance).run(instance.getDOMNode()); + getButton(component).run(component.getDOMNode()); }); - instance.update(); - + component.update(); const onTitleDuplicate = jest.fn(); - await act(async () => { - instance.find(SavedObjectSaveModal).prop('onSave')({ + component.find(SavedObjectSaveModal).prop('onSave')({ onTitleDuplicate, isTitleDuplicateConfirmed: false, newCopyOnSave: false, @@ -784,9 +858,8 @@ describe('Lens App', () => { newTitle: 'test', }); }); - expect(checkForDuplicateTitle).toHaveBeenCalledWith( - expect.objectContaining({ id: '123' }), + expect.objectContaining({ savedObjectId: '123' }), false, onTitleDuplicate, expect.anything() @@ -794,11 +867,7 @@ describe('Lens App', () => { }); it('does not show the copy button on first save', async () => { - const args = defaultArgs; - args.editorFrame = frame; - - instance = mount(); - + const { component, frame } = mountWith({}); const onChange = frame.mount.mock.calls[0][1].onChange; await act(async () => onChange({ @@ -807,36 +876,17 @@ describe('Lens App', () => { isSaveable: true, }) ); - instance.update(); - await act(async () => getButton(instance).run(instance.getDOMNode())); - instance.update(); - - expect(instance.find(SavedObjectSaveModal).prop('showCopyOnSave')).toEqual(false); + component.update(); + await act(async () => getButton(component).run(component.getDOMNode())); + component.update(); + expect(component.find(SavedObjectSaveModal).prop('showCopyOnSave')).toEqual(false); }); }); }); describe('query bar state management', () => { - let defaultArgs: ReturnType; - - beforeEach(() => { - defaultArgs = makeDefaultArgs(); - (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', - title: 'My cool doc', - expression: 'valid expression', - state: { - query: 'kuery', - }, - } as jest.ResolvedValue); - }); - it('uses the default time and query language settings', () => { - const args = defaultArgs; - args.editorFrame = frame; - - mount(); - + const { frame } = mountWith({}); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ query: { query: '', language: 'kuery' }, @@ -855,20 +905,14 @@ describe('Lens App', () => { }); it('updates the index patterns when the editor frame is changed', async () => { - const args = defaultArgs; - args.editorFrame = frame; - - instance = mount(); - + const { component, frame } = mountWith({}); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ indexPatterns: [], }), {} ); - const onChange = frame.mount.mock.calls[0][1].onChange; - await act(async () => { onChange({ filterableIndexPatterns: ['1'], @@ -876,18 +920,14 @@ describe('Lens App', () => { isSaveable: true, }); }); - - instance.update(); - + component.update(); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ indexPatterns: [{ id: '1' }], }), {} ); - // Do it again to verify that the dirty checking is done right - await act(async () => { onChange({ filterableIndexPatterns: ['2'], @@ -895,9 +935,7 @@ describe('Lens App', () => { isSaveable: true, }); }); - - instance.update(); - + component.update(); expect(TopNavMenu).toHaveBeenLastCalledWith( expect.objectContaining({ indexPatterns: [{ id: '2' }], @@ -905,21 +943,16 @@ describe('Lens App', () => { {} ); }); - it('updates the editor frame when the user changes query or time in the search bar', () => { - const args = defaultArgs; - args.editorFrame = frame; - - instance = mount(); + it('updates the editor frame when the user changes query or time in the search bar', () => { + const { component, frame } = mountWith({}); act(() => - instance.find(TopNavMenu).prop('onQuerySubmit')!({ + component.find(TopNavMenu).prop('onQuerySubmit')!({ dateRange: { from: 'now-14d', to: 'now-7d' }, query: { query: 'new', language: 'lucene' }, }) ); - - instance.update(); - + component.update(); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ query: { query: 'new', language: 'lucene' }, @@ -938,19 +971,15 @@ describe('Lens App', () => { }); it('updates the filters when the user changes them', () => { - const args = defaultArgs; - args.editorFrame = frame; - - instance = mount(); + const { component, frame, services } = mountWith({}); const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; const field = ({ name: 'myfield' } as unknown) as IFieldType; - act(() => - args.data.query.filterManager.setFilters([esFilters.buildExistsFilter(field, indexPattern)]) + services.data.query.filterManager.setFilters([ + esFilters.buildExistsFilter(field, indexPattern), + ]) ); - - instance.update(); - + component.update(); expect(frame.mount).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ @@ -962,17 +991,15 @@ describe('Lens App', () => { describe('saved query handling', () => { it('does not allow saving when the user is missing the saveQuery permission', () => { - const args = makeDefaultArgs(); - args.core.application = { - ...args.core.application, + const services = makeDefaultServices(); + services.application = { + ...services.application, capabilities: { - ...args.core.application.capabilities, + ...services.application.capabilities, visualize: { save: false, saveQuery: false, show: true }, }, }; - - mount(); - + mountWith({ services }); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ showSaveQuery: false }), {} @@ -980,11 +1007,7 @@ describe('Lens App', () => { }); it('persists the saved query ID when the query is saved', () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - - instance = mount(); - + const { component } = mountWith({}); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ showSaveQuery: true, @@ -995,9 +1018,8 @@ describe('Lens App', () => { }), {} ); - act(() => { - instance.find(TopNavMenu).prop('onSaved')!({ + component.find(TopNavMenu).prop('onSaved')!({ id: '1', attributes: { title: '', @@ -1006,7 +1028,6 @@ describe('Lens App', () => { }, }); }); - expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ savedQuery: { @@ -1023,13 +1044,9 @@ describe('Lens App', () => { }); it('changes the saved query ID when the query is updated', () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - - instance = mount(); - + const { component } = mountWith({}); act(() => { - instance.find(TopNavMenu).prop('onSaved')!({ + component.find(TopNavMenu).prop('onSaved')!({ id: '1', attributes: { title: '', @@ -1038,9 +1055,8 @@ describe('Lens App', () => { }, }); }); - act(() => { - instance.find(TopNavMenu).prop('onSavedQueryUpdated')!({ + component.find(TopNavMenu).prop('onSavedQueryUpdated')!({ id: '2', attributes: { title: 'new title', @@ -1049,7 +1065,6 @@ describe('Lens App', () => { }, }); }); - expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ savedQuery: { @@ -1066,32 +1081,23 @@ describe('Lens App', () => { }); it('clears all existing unpinned filters when the active saved query is cleared', () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - - instance = mount(); - + const { component, frame, services } = mountWith({}); act(() => - instance.find(TopNavMenu).prop('onQuerySubmit')!({ + component.find(TopNavMenu).prop('onQuerySubmit')!({ dateRange: { from: 'now-14d', to: 'now-7d' }, query: { query: 'new', language: 'lucene' }, }) ); - const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; const field = ({ name: 'myfield' } as unknown) as IFieldType; const pinnedField = ({ name: 'pinnedField' } as unknown) as IFieldType; - const unpinned = esFilters.buildExistsFilter(field, indexPattern); const pinned = esFilters.buildExistsFilter(pinnedField, indexPattern); FilterManager.setFiltersStore([pinned], esFilters.FilterStateStore.GLOBAL_STATE); - - act(() => args.data.query.filterManager.setFilters([pinned, unpinned])); - instance.update(); - - act(() => instance.find(TopNavMenu).prop('onClearSavedQuery')!()); - instance.update(); - + act(() => services.data.query.filterManager.setFilters([pinned, unpinned])); + component.update(); + act(() => component.find(TopNavMenu).prop('onClearSavedQuery')!()); + component.update(); expect(frame.mount).toHaveBeenLastCalledWith( expect.any(Element), expect.objectContaining({ @@ -1101,191 +1107,127 @@ describe('Lens App', () => { }); }); - it('displays errors from the frame in a toast', () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - - instance = mount(); - - const onError = frame.mount.mock.calls[0][1].onError; - onError({ message: 'error' }); - - instance.update(); - - expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled(); - }); - describe('showing a confirm message when leaving', () => { - let defaultArgs: ReturnType; let defaultLeave: jest.Mock; let confirmLeave: jest.Mock; beforeEach(() => { - defaultArgs = makeDefaultArgs(); defaultLeave = jest.fn(); confirmLeave = jest.fn(); - (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', - title: 'My cool doc', - state: { - query: 'kuery', - filters: [], - }, - references: [], - } as jest.ResolvedValue); }); it('should not show a confirm message if there is no expression to save', () => { - instance = mount(); - - const lastCall = - defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + const { props } = mountWith({}); + const lastCall = props.onAppLeave.mock.calls[props.onAppLeave.mock.calls.length - 1][0]; lastCall({ default: defaultLeave, confirm: confirmLeave }); - expect(defaultLeave).toHaveBeenCalled(); expect(confirmLeave).not.toHaveBeenCalled(); }); it('does not confirm if the user is missing save permissions', () => { - const args = defaultArgs; - args.core.application = { - ...args.core.application, + const services = makeDefaultServices(); + services.application = { + ...services.application, capabilities: { - ...args.core.application.capabilities, + ...services.application.capabilities, visualize: { save: false, saveQuery: false, show: true }, }, }; - args.editorFrame = frame; - - instance = mount(); - + const { component, frame, props } = mountWith({ services }); const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], doc: ({ - id: undefined, - + savedObjectId: undefined, references: [], } as unknown) as Document, isSaveable: true, }) ); - instance.update(); - - const lastCall = - defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + component.update(); + const lastCall = props.onAppLeave.mock.calls[props.onAppLeave.mock.calls.length - 1][0]; lastCall({ default: defaultLeave, confirm: confirmLeave }); - expect(defaultLeave).toHaveBeenCalled(); expect(confirmLeave).not.toHaveBeenCalled(); }); it('should confirm when leaving with an unsaved doc', () => { - defaultArgs.editorFrame = frame; - instance = mount(); - + const { component, frame, props } = mountWith({}); const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], - doc: ({ id: undefined, state: {} } as unknown) as Document, + doc: ({ savedObjectId: undefined, state: {} } as unknown) as Document, isSaveable: true, }) ); - instance.update(); - - const lastCall = - defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + component.update(); + const lastCall = props.onAppLeave.mock.calls[props.onAppLeave.mock.calls.length - 1][0]; lastCall({ default: defaultLeave, confirm: confirmLeave }); - expect(confirmLeave).toHaveBeenCalled(); expect(defaultLeave).not.toHaveBeenCalled(); }); it('should confirm when leaving with unsaved changes to an existing doc', async () => { - defaultArgs.editorFrame = frame; - instance = mount(); + const { component, frame, props } = mountWith({}); await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], doc: ({ - id: '1234', - + savedObjectId: defaultSavedObjectId, references: [], } as unknown) as Document, isSaveable: true, }) ); - instance.update(); - - const lastCall = - defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + component.update(); + const lastCall = props.onAppLeave.mock.calls[props.onAppLeave.mock.calls.length - 1][0]; lastCall({ default: defaultLeave, confirm: confirmLeave }); - expect(confirmLeave).toHaveBeenCalled(); expect(defaultLeave).not.toHaveBeenCalled(); }); it('should not confirm when changes are saved', async () => { - defaultArgs.editorFrame = frame; - instance = mount(); + const { component, frame, props } = mountWith({}); await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], - doc: ({ - id: '1234', - title: 'My cool doc', - references: [], - state: { - query: 'kuery', - filters: [], - }, - } as unknown) as Document, + doc: defaultDoc, isSaveable: true, }) ); - instance.update(); - - const lastCall = - defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + component.update(); + const lastCall = props.onAppLeave.mock.calls[props.onAppLeave.mock.calls.length - 1][0]; lastCall({ default: defaultLeave, confirm: confirmLeave }); - expect(defaultLeave).toHaveBeenCalled(); expect(confirmLeave).not.toHaveBeenCalled(); }); it('should confirm when the latest doc is invalid', async () => { - defaultArgs.editorFrame = frame; - instance = mount(); + const { component, frame, props } = mountWith({}); await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], - doc: ({ id: '1234', references: [] } as unknown) as Document, + doc: ({ savedObjectId: defaultSavedObjectId, references: [] } as unknown) as Document, isSaveable: true, }) ); - instance.update(); - - const lastCall = - defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + component.update(); + const lastCall = props.onAppLeave.mock.calls[props.onAppLeave.mock.calls.length - 1][0]; lastCall({ default: defaultLeave, confirm: confirmLeave }); - expect(confirmLeave).toHaveBeenCalled(); expect(defaultLeave).not.toHaveBeenCalled(); }); diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index bfdf4ceaaabd32..d2ccbe0cb2fee8 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -6,108 +6,82 @@ import _ from 'lodash'; import React, { useState, useEffect, useCallback } from 'react'; -import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { AppMountContext, AppMountParameters, NotificationsStart } from 'kibana/public'; -import { History } from 'history'; +import { NotificationsStart } from 'kibana/public'; import { EuiBreadcrumb } from '@elastic/eui'; -import { - Query, - DataPublicPluginStart, - syncQueryStateWithUrl, -} from '../../../../../src/plugins/data/public'; import { createKbnUrlStateStorage, - IStorageWrapper, withNotifyOnErrors, } from '../../../../../src/plugins/kibana_utils/public'; -import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; import { - SavedObjectSaveModalOrigin, OnSaveProps, checkForDuplicateTitle, + SavedObjectSaveModalOrigin, } from '../../../../../src/plugins/saved_objects/public'; -import { Document, SavedObjectStore, injectFilterReferences } from '../persistence'; -import { EditorFrameInstance } from '../types'; +import { injectFilterReferences } from '../persistence'; import { NativeRenderer } from '../native_renderer'; import { trackUiEvent } from '../lens_ui_telemetry'; import { esFilters, - Filter, IndexPattern as IndexPatternInstance, IndexPatternsContract, - SavedQuery, + syncQueryStateWithUrl, } from '../../../../../src/plugins/data/public'; -import { getFullPath } from '../../common'; - -interface State { - indicateNoData: boolean; - isLoading: boolean; - isSaveModalVisible: boolean; - indexPatternsForTopNav: IndexPatternInstance[]; - originatingApp?: string; - persistedDoc?: Document; - lastKnownDoc?: Document; - - // Properties needed to interface with TopNav - dateRange: { - fromDate: string; - toDate: string; - }; - query: Query; - filters: Filter[]; - savedQuery?: SavedQuery; - isSaveable: boolean; -} +import { LENS_EMBEDDABLE_TYPE, getFullPath } from '../../common'; +import { LensAppProps, LensAppServices, LensAppState } from './types'; +import { getLensTopNavConfig } from './lens_top_nav'; +import { + LensByReferenceInput, + LensEmbeddableInput, +} from '../editor_frame_service/embeddable/embeddable'; export function App({ - editorFrame, - data, - core, - storage, - docId, - docStorage, - redirectTo, - originatingApp, - navigation, + history, onAppLeave, + redirectTo, + editorFrame, + initialInput, + incomingState, + redirectToOrigin, setHeaderActionMenu, - history, - getAppNameFromId, -}: { - editorFrame: EditorFrameInstance; - data: DataPublicPluginStart; - navigation: NavigationPublicPluginStart; - core: AppMountContext['core']; - storage: IStorageWrapper; - docId?: string; - docStorage: SavedObjectStore; - redirectTo: (id?: string, returnToOrigin?: boolean, newlyCreated?: boolean) => void; - originatingApp?: string | undefined; - onAppLeave: AppMountParameters['onAppLeave']; - setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; - history: History; - getAppNameFromId?: (appId: string) => string | undefined; -}) { - const [state, setState] = useState(() => { +}: LensAppProps) { + const { + data, + chrome, + overlays, + navigation, + uiSettings, + application, + notifications, + attributeService, + savedObjectsClient, + getOriginatingAppName, + + // Temporarily required until the 'by value' paradigm is default. + dashboardFeatureFlag, + } = useKibana().services; + + const [state, setState] = useState(() => { const currentRange = data.query.timefilter.timefilter.getTime(); return { - isLoading: !!docId, - isSaveModalVisible: false, - indexPatternsForTopNav: [], query: data.query.queryString.getDefaultQuery(), + filters: data.query.filterManager.getFilters(), + isLoading: Boolean(initialInput), + indexPatternsForTopNav: [], dateRange: { fromDate: currentRange.from, toDate: currentRange.to, }, - originatingApp, - filters: data.query.filterManager.getFilters(), + isLinkedToOriginatingApp: Boolean(incomingState?.originatingApp), + isSaveModalVisible: false, indicateNoData: false, isSaveable: false, }; }); + const { lastKnownDoc } = state; + const showNoDataPopover = useCallback(() => { setState((prevState) => ({ ...prevState, indicateNoData: true })); }, [setState]); @@ -125,9 +99,44 @@ export function App({ state.indexPatternsForTopNav, ]); - const { lastKnownDoc } = state; + const onError = useCallback( + (e: { message: string }) => + notifications.toasts.addDanger({ + title: e.message, + }), + [notifications.toasts] + ); + + const getLastKnownDocWithoutPinnedFilters = useCallback( + function () { + if (!lastKnownDoc) return undefined; + const [pinnedFilters, appFilters] = _.partition( + injectFilterReferences(lastKnownDoc.state?.filters || [], lastKnownDoc.references), + esFilters.isFilterPinned + ); + return pinnedFilters?.length + ? { + ...lastKnownDoc, + state: { + ...lastKnownDoc.state, + filters: appFilters, + }, + } + : lastKnownDoc; + }, + [lastKnownDoc] + ); - const savingPermitted = state.isSaveable && core.application.capabilities.visualize.save; + const getIsByValueMode = useCallback( + () => + Boolean( + // Temporarily required until the 'by value' paradigm is default. + dashboardFeatureFlag.allowByValueEmbeddables && + state.isLinkedToOriginatingApp && + !(initialInput as LensByReferenceInput)?.savedObjectId + ), + [dashboardFeatureFlag.allowByValueEmbeddables, state.isLinkedToOriginatingApp, initialInput] + ); useEffect(() => { // Clear app-specific filters when navigating to Lens. Necessary because Lens @@ -156,8 +165,8 @@ export function App({ const kbnUrlStateStorage = createKbnUrlStateStorage({ history, - useHash: core.uiSettings.get('state:storeInSessionStorage'), - ...withNotifyOnErrors(core.notifications.toasts), + useHash: uiSettings.get('state:storeInSessionStorage'), + ...withNotifyOnErrors(notifications.toasts), }); const { stop: stopSyncingQueryServiceStateWithUrl } = syncQueryStateWithUrl( data.query, @@ -172,38 +181,18 @@ export function App({ }, [ data.query.filterManager, data.query.timefilter.timefilter, - core.notifications.toasts, - core.uiSettings, + notifications.toasts, + uiSettings, data.query, history, ]); - const getLastKnownDocWithoutPinnedFilters = useCallback( - function () { - if (!lastKnownDoc) return undefined; - const [pinnedFilters, appFilters] = _.partition( - injectFilterReferences(lastKnownDoc.state?.filters || [], lastKnownDoc.references), - esFilters.isFilterPinned - ); - return pinnedFilters?.length - ? { - ...lastKnownDoc, - state: { - ...lastKnownDoc.state, - filters: appFilters, - }, - } - : lastKnownDoc; - }, - [lastKnownDoc] - ); - useEffect(() => { onAppLeave((actions) => { // Confirm when the user has made any changes to an existing doc // or when the user has configured something without saving if ( - core.application.capabilities.visualize.save && + application.capabilities.visualize.save && !_.isEqual(state.persistedDoc?.state, getLastKnownDocWithoutPinnedFilters()?.state) && (state.isSaveable || state.persistedDoc) ) { @@ -220,379 +209,430 @@ export function App({ } }); }, [ - lastKnownDoc, onAppLeave, - state.persistedDoc, + lastKnownDoc, state.isSaveable, - core.application.capabilities.visualize.save, + state.persistedDoc, getLastKnownDocWithoutPinnedFilters, + application.capabilities.visualize.save, ]); // Sync Kibana breadcrumbs any time the saved document's title changes useEffect(() => { - core.chrome.setBreadcrumbs([ - ...(originatingApp && getAppNameFromId - ? [ - { - onClick: (e) => { - core.application.navigateToApp(originatingApp); - }, - text: getAppNameFromId(originatingApp), - } as EuiBreadcrumb, - ] - : []), - { - href: core.http.basePath.prepend(`/app/visualize#/`), + const isByValueMode = getIsByValueMode(); + const breadcrumbs: EuiBreadcrumb[] = []; + if (state.isLinkedToOriginatingApp && getOriginatingAppName() && redirectToOrigin) { + breadcrumbs.push({ + onClick: () => { + redirectToOrigin(); + }, + text: getOriginatingAppName(), + }); + } + if (!isByValueMode) { + breadcrumbs.push({ + href: application.getUrlForApp('visualize'), onClick: (e) => { - core.application.navigateToApp('visualize', { path: '/' }); + application.navigateToApp('visualize', { path: '/' }); e.preventDefault(); }, text: i18n.translate('xpack.lens.breadcrumbsTitle', { defaultMessage: 'Visualize', }), - }, - { - text: state.persistedDoc - ? state.persistedDoc.title - : i18n.translate('xpack.lens.breadcrumbsCreate', { defaultMessage: 'Create' }), - }, - ]); + }); + } + let currentDocTitle = i18n.translate('xpack.lens.breadcrumbsCreate', { + defaultMessage: 'Create', + }); + if (state.persistedDoc) { + currentDocTitle = isByValueMode + ? i18n.translate('xpack.lens.breadcrumbsByValue', { defaultMessage: 'Edit visualization' }) + : state.persistedDoc.title; + } + breadcrumbs.push({ text: currentDocTitle }); + chrome.setBreadcrumbs(breadcrumbs); }, [ - core.application, - core.chrome, - core.http.basePath, + dashboardFeatureFlag.allowByValueEmbeddables, + state.isLinkedToOriginatingApp, + getOriginatingAppName, state.persistedDoc, - originatingApp, - redirectTo, - getAppNameFromId, + redirectToOrigin, + getIsByValueMode, + initialInput, + application, + chrome, ]); - useEffect( - () => { - if (docId && (!state.persistedDoc || state.persistedDoc.id !== docId)) { - setState((s) => ({ ...s, isLoading: true })); - docStorage - .load(docId) - .then((doc) => { - core.chrome.recentlyAccessed.add(getFullPath(docId), doc.title, docId); - getAllIndexPatterns( - _.uniq( - doc.references.filter(({ type }) => type === 'index-pattern').map(({ id }) => id) - ), - data.indexPatterns, - core.notifications - ) - .then((indexPatterns) => { - // Don't overwrite any pinned filters - data.query.filterManager.setAppFilters( - injectFilterReferences(doc.state.filters, doc.references) - ); - setState((s) => ({ - ...s, - isLoading: false, - persistedDoc: doc, - lastKnownDoc: doc, - query: doc.state.query, - indexPatternsForTopNav: indexPatterns, - })); - }) - .catch((e) => { - setState((s) => ({ ...s, isLoading: false })); - - redirectTo(); - }); + useEffect(() => { + if ( + !initialInput || + (attributeService.inputIsRefType(initialInput) && + initialInput.savedObjectId === state.persistedDoc?.savedObjectId) + ) { + return; + } + + setState((s) => ({ ...s, isLoading: true })); + attributeService + .unwrapAttributes(initialInput) + .then((attributes) => { + if (!initialInput) { + return; + } + const doc = { + ...initialInput, + ...attributes, + type: LENS_EMBEDDABLE_TYPE, + }; + + if (attributeService.inputIsRefType(initialInput)) { + chrome.recentlyAccessed.add( + getFullPath(initialInput.savedObjectId), + attributes.title, + initialInput.savedObjectId + ); + } + getAllIndexPatterns( + _.uniq(doc.references.filter(({ type }) => type === 'index-pattern').map(({ id }) => id)), + data.indexPatterns, + notifications + ) + .then((indexPatterns) => { + // Don't overwrite any pinned filters + data.query.filterManager.setAppFilters( + injectFilterReferences(doc.state.filters, doc.references) + ); + setState((s) => ({ + ...s, + isLoading: false, + persistedDoc: doc, + lastKnownDoc: doc, + query: doc.state.query, + indexPatternsForTopNav: indexPatterns, + })); }) .catch((e) => { setState((s) => ({ ...s, isLoading: false })); - - core.notifications.toasts.addDanger( - i18n.translate('xpack.lens.app.docLoadingError', { - defaultMessage: 'Error loading saved document', - }) - ); - redirectTo(); }); - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [ - core.notifications, - data.indexPatterns, - data.query.filterManager, - docId, - // TODO: These dependencies are changing too often - // docStorage, - // redirectTo, - // state.persistedDoc, - ] - ); + }) + .catch((e) => { + setState((s) => ({ ...s, isLoading: false })); + notifications.toasts.addDanger( + i18n.translate('xpack.lens.app.docLoadingError', { + defaultMessage: 'Error loading saved document', + }) + ); + + redirectTo(); + }); + }, [ + notifications, + data.indexPatterns, + data.query.filterManager, + initialInput, + attributeService, + redirectTo, + chrome.recentlyAccessed, + state.persistedDoc?.savedObjectId, + state.persistedDoc?.state, + ]); const runSave = async ( saveProps: Omit & { returnToOrigin: boolean; onTitleDuplicate?: OnSaveProps['onTitleDuplicate']; newDescription?: string; - } + }, + options: { saveToLibrary: boolean } ) => { if (!lastKnownDoc) { return; } - - const doc = { + const docToSave = { ...getLastKnownDocWithoutPinnedFilters()!, description: saveProps.newDescription, - id: saveProps.newCopyOnSave ? undefined : lastKnownDoc.id, + savedObjectId: saveProps.newCopyOnSave ? undefined : lastKnownDoc.savedObjectId, title: saveProps.newTitle, }; - await checkForDuplicateTitle( - { - ...doc, - copyOnSave: saveProps.newCopyOnSave, - lastSavedTitle: lastKnownDoc?.title, - getEsType: () => 'lens', - getDisplayName: () => - i18n.translate('xpack.lens.app.saveModalType', { - defaultMessage: 'Lens visualization', - }), - }, - saveProps.isTitleDuplicateConfirmed, - saveProps.onTitleDuplicate, - { - savedObjectsClient: core.savedObjects.client, - overlays: core.overlays, + // Required to serialize filters in by value mode until + // https://github.com/elastic/kibana/issues/77588 is fixed + if (getIsByValueMode()) { + docToSave.state.filters.forEach((filter) => { + if (typeof filter.meta.value === 'function') { + delete filter.meta.value; + } + }); + } + + const originalInput = saveProps.newCopyOnSave ? undefined : initialInput; + const originalSavedObjectId = (originalInput as LensByReferenceInput)?.savedObjectId; + if (options.saveToLibrary && !originalInput) { + await checkForDuplicateTitle( + { + ...docToSave, + copyOnSave: saveProps.newCopyOnSave, + lastSavedTitle: lastKnownDoc.title, + getEsType: () => 'lens', + getDisplayName: () => + i18n.translate('xpack.lens.app.saveModalType', { + defaultMessage: 'Lens visualization', + }), + }, + saveProps.isTitleDuplicateConfirmed, + saveProps.onTitleDuplicate, + { + savedObjectsClient, + overlays, + } + ); + } + try { + const newInput = (await attributeService.wrapAttributes( + docToSave, + options.saveToLibrary, + originalInput + )) as LensEmbeddableInput; + + if (saveProps.returnToOrigin && redirectToOrigin) { + // disabling the validation on app leave because the document has been saved. + onAppLeave((actions) => { + return actions.default(); + }); + redirectToOrigin({ input: newInput, isCopied: saveProps.newCopyOnSave }); + return; } - ); - const newlyCreated: boolean = saveProps.newCopyOnSave || !lastKnownDoc?.id; - docStorage - .save(doc) - .then(({ id }) => { - core.chrome.recentlyAccessed.add(getFullPath(id), doc.title, id); - // Prevents unnecessary network request and disables save button - const newDoc = { ...doc, id }; - const currentOriginatingApp = state.originatingApp; + if ( + attributeService.inputIsRefType(newInput) && + newInput.savedObjectId !== originalSavedObjectId + ) { + chrome.recentlyAccessed.add( + getFullPath(newInput.savedObjectId), + docToSave.title, + newInput.savedObjectId + ); setState((s) => ({ ...s, isSaveModalVisible: false, - originatingApp: - newlyCreated && !saveProps.returnToOrigin ? undefined : currentOriginatingApp, - persistedDoc: newDoc, - lastKnownDoc: newDoc, + isLinkedToOriginatingApp: false, })); - if (docId !== id || saveProps.returnToOrigin) { - redirectTo(id, saveProps.returnToOrigin, newlyCreated); - } - }) - .catch((e) => { - // eslint-disable-next-line no-console - console.dir(e); - trackUiEvent('save_failed'); - core.notifications.toasts.addDanger( - i18n.translate('xpack.lens.app.docSavingError', { - defaultMessage: 'Error saving document', - }) - ); - setState((s) => ({ ...s, isSaveModalVisible: false })); - }); - }; + redirectTo(newInput.savedObjectId); + return; + } - const onError = useCallback( - (e: { message: string }) => - core.notifications.toasts.addDanger({ - title: e.message, - }), - [core.notifications.toasts] - ); + const newDoc = { + ...docToSave, + ...newInput, + }; + setState((s) => ({ + ...s, + persistedDoc: newDoc, + lastKnownDoc: newDoc, + isSaveModalVisible: false, + isLinkedToOriginatingApp: false, + })); + } catch (e) { + // eslint-disable-next-line no-console + console.dir(e); + trackUiEvent('save_failed'); + notifications.toasts.addDanger( + i18n.translate('xpack.lens.app.docSavingError', { + defaultMessage: 'Error saving document', + }) + ); + setState((s) => ({ ...s, isSaveModalVisible: false })); + } + }; const { TopNavMenu } = navigation.ui; + const savingPermitted = Boolean(state.isSaveable && application.capabilities.visualize.save); + const topNavConfig = getLensTopNavConfig({ + showSaveAndReturn: Boolean( + state.isLinkedToOriginatingApp && + // Temporarily required until the 'by value' paradigm is default. + (dashboardFeatureFlag.allowByValueEmbeddables || Boolean(initialInput)) + ), + isByValueMode: getIsByValueMode(), + showCancel: Boolean(state.isLinkedToOriginatingApp), + savingPermitted, + actions: { + saveAndReturn: () => { + if (savingPermitted && lastKnownDoc) { + // disabling the validation on app leave because the document has been saved. + onAppLeave((actions) => { + return actions.default(); + }); + runSave( + { + newTitle: lastKnownDoc.title, + newCopyOnSave: false, + isTitleDuplicateConfirmed: false, + returnToOrigin: true, + }, + { + saveToLibrary: + (initialInput && attributeService.inputIsRefType(initialInput)) ?? false, + } + ); + } + }, + showSaveModal: () => { + if (savingPermitted) { + setState((s) => ({ ...s, isSaveModalVisible: true })); + } + }, + cancel: () => { + if (redirectToOrigin) { + redirectToOrigin(); + } + }, + }, + }); + return ( - - -
-
- { - if (savingPermitted) { - runSave({ - newTitle: lastKnownDoc.title, - newCopyOnSave: false, - isTitleDuplicateConfirmed: false, - returnToOrigin: true, - }); - } - }, - testId: 'lnsApp_saveAndReturnButton', - disableButton: !savingPermitted, - }, - ] - : []), - { - label: - lastKnownDoc?.id && !!state.originatingApp - ? i18n.translate('xpack.lens.app.saveAs', { - defaultMessage: 'Save as', - }) - : i18n.translate('xpack.lens.app.save', { - defaultMessage: 'Save', - }), - emphasize: !state.originatingApp || !lastKnownDoc?.id, - run: () => { - if (savingPermitted) { - setState((s) => ({ ...s, isSaveModalVisible: true })); - } - }, - testId: 'lnsApp_saveButton', - disableButton: !savingPermitted, + <> +
+
+ { + const { dateRange, query } = payload; + if ( + dateRange.from !== state.dateRange.fromDate || + dateRange.to !== state.dateRange.toDate + ) { + data.query.timefilter.timefilter.setTime(dateRange); + trackUiEvent('app_date_change'); + } else { + trackUiEvent('app_query_change'); + } + setState((s) => ({ + ...s, + dateRange: { + fromDate: dateRange.from, + toDate: dateRange.to, }, - ]} - data-test-subj="lnsApp_topNav" - screenTitle={'lens'} - onQuerySubmit={(payload) => { - const { dateRange, query } = payload; + query: query || s.query, + })); + }} + onSaved={(savedQuery) => { + setState((s) => ({ ...s, savedQuery })); + }} + onSavedQueryUpdated={(savedQuery) => { + const savedQueryFilters = savedQuery.attributes.filters || []; + const globalFilters = data.query.filterManager.getGlobalFilters(); + data.query.filterManager.setFilters([...globalFilters, ...savedQueryFilters]); + setState((s) => ({ + ...s, + savedQuery: { ...savedQuery }, // Shallow query for reference issues + dateRange: savedQuery.attributes.timefilter + ? { + fromDate: savedQuery.attributes.timefilter.from, + toDate: savedQuery.attributes.timefilter.to, + } + : s.dateRange, + })); + }} + onClearSavedQuery={() => { + data.query.filterManager.setFilters(data.query.filterManager.getGlobalFilters()); + setState((s) => ({ + ...s, + savedQuery: undefined, + filters: data.query.filterManager.getGlobalFilters(), + query: data.query.queryString.getDefaultQuery(), + })); + }} + query={state.query} + dateRangeFrom={state.dateRange.fromDate} + dateRangeTo={state.dateRange.toDate} + indicateNoData={state.indicateNoData} + /> +
+ {(!state.isLoading || state.persistedDoc) && ( + { + if (isSaveable !== state.isSaveable) { + setState((s) => ({ ...s, isSaveable })); + } + if (!_.isEqual(state.persistedDoc, doc)) { + setState((s) => ({ ...s, lastKnownDoc: doc })); + } + // Update the cached index patterns if the user made a change to any of them if ( - dateRange.from !== state.dateRange.fromDate || - dateRange.to !== state.dateRange.toDate + state.indexPatternsForTopNav.length !== filterableIndexPatterns.length || + filterableIndexPatterns.some( + (id) => + !state.indexPatternsForTopNav.find((indexPattern) => indexPattern.id === id) + ) ) { - data.query.timefilter.timefilter.setTime(dateRange); - trackUiEvent('app_date_change'); - } else { - trackUiEvent('app_query_change'); + getAllIndexPatterns( + filterableIndexPatterns, + data.indexPatterns, + notifications + ).then((indexPatterns) => { + if (indexPatterns) { + setState((s) => ({ ...s, indexPatternsForTopNav: indexPatterns })); + } + }); } - - setState((s) => ({ - ...s, - dateRange: { - fromDate: dateRange.from, - toDate: dateRange.to, - }, - query: query || s.query, - })); - }} - appName={'lens'} - indexPatterns={state.indexPatternsForTopNav} - showSearchBar={true} - showDatePicker={true} - showQueryBar={true} - showFilterBar={true} - showSaveQuery={core.application.capabilities.visualize.saveQuery as boolean} - savedQuery={state.savedQuery} - onSaved={(savedQuery) => { - setState((s) => ({ ...s, savedQuery })); - }} - onSavedQueryUpdated={(savedQuery) => { - const savedQueryFilters = savedQuery.attributes.filters || []; - const globalFilters = data.query.filterManager.getGlobalFilters(); - data.query.filterManager.setFilters([...globalFilters, ...savedQueryFilters]); - setState((s) => ({ - ...s, - savedQuery: { ...savedQuery }, // Shallow query for reference issues - dateRange: savedQuery.attributes.timefilter - ? { - fromDate: savedQuery.attributes.timefilter.from, - toDate: savedQuery.attributes.timefilter.to, - } - : s.dateRange, - })); - }} - onClearSavedQuery={() => { - data.query.filterManager.setFilters(data.query.filterManager.getGlobalFilters()); - setState((s) => ({ - ...s, - savedQuery: undefined, - filters: data.query.filterManager.getGlobalFilters(), - query: data.query.queryString.getDefaultQuery(), - })); - }} - query={state.query} - dateRangeFrom={state.dateRange.fromDate} - dateRangeTo={state.dateRange.toDate} - indicateNoData={state.indicateNoData} - /> -
- - {(!state.isLoading || state.persistedDoc) && ( - { - if (isSaveable !== state.isSaveable) { - setState((s) => ({ ...s, isSaveable })); - } - if (!_.isEqual(state.persistedDoc, doc)) { - setState((s) => ({ ...s, lastKnownDoc: doc })); - } - - // Update the cached index patterns if the user made a change to any of them - if ( - state.indexPatternsForTopNav.length !== filterableIndexPatterns.length || - filterableIndexPatterns.some( - (id) => - !state.indexPatternsForTopNav.find((indexPattern) => indexPattern.id === id) - ) - ) { - getAllIndexPatterns( - filterableIndexPatterns, - data.indexPatterns, - core.notifications - ).then((indexPatterns) => { - if (indexPatterns) { - setState((s) => ({ ...s, indexPatternsForTopNav: indexPatterns })); - } - }); - } - }, - }} - /> - )} -
- {lastKnownDoc && state.isSaveModalVisible && ( - runSave(props)} - onClose={() => setState((s) => ({ ...s, isSaveModalVisible: false }))} - getAppNameFromId={getAppNameFromId} - documentInfo={{ - id: lastKnownDoc.id, - title: lastKnownDoc.title || '', - description: lastKnownDoc.description || '', + }, }} - objectType={i18n.translate('xpack.lens.app.saveModalType', { - defaultMessage: 'Lens visualization', - })} - data-test-subj="lnsApp_saveModalOrigin" /> )} - - +
+ {lastKnownDoc && state.isSaveModalVisible && ( + runSave(props, { saveToLibrary: true })} + onClose={() => { + setState((s) => ({ ...s, isSaveModalVisible: false })); + }} + getAppNameFromId={() => getOriginatingAppName()} + documentInfo={{ + id: lastKnownDoc.savedObjectId, + title: lastKnownDoc.title || '', + description: lastKnownDoc.description || '', + }} + returnToOriginSwitchLabel={ + getIsByValueMode() && initialInput + ? i18n.translate('xpack.lens.app.updatePanel', { + defaultMessage: 'Update panel on {originatingAppName}', + values: { originatingAppName: getOriginatingAppName() }, + }) + : undefined + } + objectType={i18n.translate('xpack.lens.app.saveModalType', { + defaultMessage: 'Lens visualization', + })} + data-test-subj="lnsApp_saveModalOrigin" + /> + )} + ); } diff --git a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx new file mode 100644 index 00000000000000..f6234d063d8cdf --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx @@ -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 { i18n } from '@kbn/i18n'; +import { TopNavMenuData } from '../../../../../src/plugins/navigation/public'; +import { LensTopNavActions } from './types'; + +export function getLensTopNavConfig(options: { + showSaveAndReturn: boolean; + showCancel: boolean; + isByValueMode: boolean; + actions: LensTopNavActions; + savingPermitted: boolean; +}): TopNavMenuData[] { + const { showSaveAndReturn, showCancel, actions, savingPermitted } = options; + const topNavMenu: TopNavMenuData[] = []; + + const saveButtonLabel = options.isByValueMode + ? i18n.translate('xpack.lens.app.addToLibrary', { + defaultMessage: 'Save to library', + }) + : options.showSaveAndReturn + ? i18n.translate('xpack.lens.app.saveAs', { + defaultMessage: 'Save as', + }) + : i18n.translate('xpack.lens.app.save', { + defaultMessage: 'Save', + }); + + if (showSaveAndReturn) { + topNavMenu.push({ + label: i18n.translate('xpack.lens.app.saveAndReturn', { + defaultMessage: 'Save and return', + }), + emphasize: true, + iconType: 'check', + run: actions.saveAndReturn, + testId: 'lnsApp_saveAndReturnButton', + disableButton: !savingPermitted, + description: i18n.translate('xpack.lens.app.saveAndReturnButtonAriaLabel', { + defaultMessage: 'Save the current lens visualization and return to the last app', + }), + }); + } + + topNavMenu.push({ + label: saveButtonLabel, + emphasize: !showSaveAndReturn, + run: actions.showSaveModal, + testId: 'lnsApp_saveButton', + description: i18n.translate('xpack.lens.app.saveButtonAriaLabel', { + defaultMessage: 'Save the current lens visualization', + }), + disableButton: !savingPermitted, + }); + + if (showCancel) { + topNavMenu.push({ + label: i18n.translate('xpack.lens.app.cancel', { + defaultMessage: 'cancel', + }), + run: actions.cancel, + testId: 'lnsApp_cancelButton', + description: i18n.translate('xpack.lens.app.cancelButtonAriaLabel', { + defaultMessage: 'Return to the last app without saving changes', + }), + }); + } + return topNavMenu; +} diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index ebc38e4929f6c9..0d50e541d3e48e 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -11,6 +11,7 @@ import { HashRouter, Route, RouteComponentProps, Switch } from 'react-router-dom import { render, unmountComponentAtNode } from 'react-dom'; import { i18n } from '@kbn/i18n'; +import { DashboardFeatureFlagConfig } from 'src/plugins/dashboard/public'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { LensReportManager, setReportManager, trackUiEvent } from '../lens_ui_telemetry'; @@ -18,76 +19,116 @@ import { LensReportManager, setReportManager, trackUiEvent } from '../lens_ui_te import { App } from './app'; import { EditorFrameStart } from '../types'; import { addHelpMenuToAppChrome } from '../help_menu_util'; -import { SavedObjectIndexStore } from '../persistence'; import { LensPluginStartDependencies } from '../plugin'; -import { LENS_EMBEDDABLE_TYPE } from '../../common'; +import { LENS_EMBEDDABLE_TYPE, LENS_EDIT_BY_VALUE } from '../../common'; +import { + LensEmbeddableInput, + LensByReferenceInput, + LensByValueInput, +} from '../editor_frame_service/embeddable/embeddable'; +import { LensAttributeService } from '../lens_attribute_service'; +import { LensAppServices, RedirectToOriginProps } from './types'; +import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; export async function mountApp( core: CoreSetup, params: AppMountParameters, - createEditorFrame: EditorFrameStart['createInstance'] + mountProps: { + createEditorFrame: EditorFrameStart['createInstance']; + getByValueFeatureFlag: () => Promise; + attributeService: LensAttributeService; + } ) { + const { createEditorFrame, getByValueFeatureFlag, attributeService } = mountProps; const [coreStart, startDependencies] = await core.getStartServices(); - const { data: dataStart, navigation, embeddable } = startDependencies; - const savedObjectsClient = coreStart.savedObjects.client; - addHelpMenuToAppChrome(coreStart.chrome, coreStart.docLinks); + const { data, navigation, embeddable } = startDependencies; + + const instance = await createEditorFrame(); + const storage = new Storage(localStorage); + const stateTransfer = embeddable?.getStateTransfer(params.history); + const embeddableEditorIncomingState = stateTransfer?.getIncomingEditorState(); + + const lensServices: LensAppServices = { + data, + storage, + navigation, + attributeService, + http: coreStart.http, + chrome: coreStart.chrome, + overlays: coreStart.overlays, + uiSettings: coreStart.uiSettings, + application: coreStart.application, + notifications: coreStart.notifications, + savedObjectsClient: coreStart.savedObjects.client, + getOriginatingAppName: () => { + return embeddableEditorIncomingState?.originatingApp + ? stateTransfer?.getAppNameFromId(embeddableEditorIncomingState.originatingApp) + : undefined; + }, + // Temporarily required until the 'by value' paradigm is default. + dashboardFeatureFlag: await getByValueFeatureFlag(), + }; + + addHelpMenuToAppChrome(coreStart.chrome, coreStart.docLinks); coreStart.chrome.docTitle.change( i18n.translate('xpack.lens.pageTitle', { defaultMessage: 'Lens' }) ); - const stateTransfer = embeddable?.getStateTransfer(params.history); - const { originatingApp } = - stateTransfer?.getIncomingEditorState({ keysToRemoveAfterFetch: ['originatingApp'] }) || {}; - - const instance = await createEditorFrame(); - setReportManager( new LensReportManager({ - storage: new Storage(localStorage), http: core.http, + storage, }) ); - const redirectTo = ( - routeProps: RouteComponentProps<{ id?: string }>, - id?: string, - returnToOrigin?: boolean, - newlyCreated?: boolean - ) => { - if (!id) { + + const getInitialInput = ( + routeProps: RouteComponentProps<{ id?: string }> + ): LensEmbeddableInput | undefined => { + if (routeProps.match.params.id) { + return { savedObjectId: routeProps.match.params.id } as LensByReferenceInput; + } + if (embeddableEditorIncomingState?.valueInput) { + return embeddableEditorIncomingState?.valueInput as LensByValueInput; + } + }; + + const redirectTo = (routeProps: RouteComponentProps<{ id?: string }>, savedObjectId?: string) => { + if (!savedObjectId) { routeProps.history.push('/'); - } else if (!originatingApp) { - routeProps.history.push(`/edit/${id}`); - } else if (!!originatingApp && id && returnToOrigin) { - routeProps.history.push(`/edit/${id}`); - - if (newlyCreated && stateTransfer) { - stateTransfer.navigateToWithEmbeddablePackage(originatingApp, { - state: { id, type: LENS_EMBEDDABLE_TYPE }, - }); - } else { - coreStart.application.navigateToApp(originatingApp); - } + } else { + routeProps.history.push(`/edit/${savedObjectId}`); } }; + const redirectToOrigin = (props?: RedirectToOriginProps) => { + if (!embeddableEditorIncomingState?.originatingApp) { + throw new Error('redirectToOrigin called without an originating app'); + } + if (stateTransfer && props?.input) { + const { input, isCopied } = props; + stateTransfer.navigateToWithEmbeddablePackage(embeddableEditorIncomingState?.originatingApp, { + state: { + embeddableId: isCopied ? undefined : embeddableEditorIncomingState.embeddableId, + type: LENS_EMBEDDABLE_TYPE, + input, + }, + }); + } else { + coreStart.application.navigateToApp(embeddableEditorIncomingState?.originatingApp); + } + }; + + // const featureFlagConfig = await getByValueFeatureFlag(); const renderEditor = (routeProps: RouteComponentProps<{ id?: string }>) => { trackUiEvent('loaded'); - return ( - redirectTo(routeProps, id, returnToOrigin, newlyCreated) - } - originatingApp={originatingApp} - getAppNameFromId={stateTransfer.getAppNameFromId} + initialInput={getInitialInput(routeProps)} + redirectTo={(savedObjectId?: string) => redirectTo(routeProps, savedObjectId)} + redirectToOrigin={redirectToOrigin} onAppLeave={params.onAppLeave} setHeaderActionMenu={params.setHeaderActionMenu} history={routeProps.history} @@ -103,13 +144,16 @@ export async function mountApp( params.element.classList.add('lnsAppWrapper'); render( - - - - - - - + + + + + + + + + + , params.element ); diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts new file mode 100644 index 00000000000000..fcdd0b20f8d276 --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/types.ts @@ -0,0 +1,102 @@ +/* + * 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 { History } from 'history'; +import { + ApplicationStart, + AppMountParameters, + ChromeStart, + HttpStart, + IUiSettingsClient, + NotificationsStart, + OverlayStart, + SavedObjectsStart, +} from '../../../../../src/core/public'; +import { + DataPublicPluginStart, + Filter, + IndexPattern, + Query, + SavedQuery, +} from '../../../../../src/plugins/data/public'; +import { Document } from '../persistence'; +import { LensEmbeddableInput } from '../editor_frame_service/embeddable/embeddable'; +import { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public'; +import { LensAttributeService } from '../lens_attribute_service'; +import { IStorageWrapper } from '../../../../../src/plugins/kibana_utils/public'; +import { DashboardFeatureFlagConfig } from '../../../../../src/plugins/dashboard/public'; +import { EmbeddableEditorState } from '../../../../../src/plugins/embeddable/public'; +import { EditorFrameInstance } from '..'; + +export interface LensAppState { + isLoading: boolean; + persistedDoc?: Document; + lastKnownDoc?: Document; + isSaveModalVisible: boolean; + + // Used to show a popover that guides the user towards changing the date range when no data is available. + indicateNoData: boolean; + + // index patterns used to determine which filters are available in the top nav. + indexPatternsForTopNav: IndexPattern[]; + + // Determines whether the lens editor shows the 'save and return' button, and the originating app breadcrumb. + isLinkedToOriginatingApp?: boolean; + + // Properties needed to interface with TopNav + dateRange: { + fromDate: string; + toDate: string; + }; + query: Query; + filters: Filter[]; + savedQuery?: SavedQuery; + isSaveable: boolean; +} + +export interface RedirectToOriginProps { + input?: LensEmbeddableInput; + isCopied?: boolean; +} + +export interface LensAppProps { + history: History; + editorFrame: EditorFrameInstance; + onAppLeave: AppMountParameters['onAppLeave']; + setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; + redirectTo: (savedObjectId?: string) => void; + redirectToOrigin?: (props?: RedirectToOriginProps) => void; + + // The initial input passed in by the container when editing. Can be either by reference or by value. + initialInput?: LensEmbeddableInput; + + // State passed in by the container which is used to determine the id of the Originating App. + incomingState?: EmbeddableEditorState; +} + +export interface LensAppServices { + http: HttpStart; + chrome: ChromeStart; + overlays: OverlayStart; + storage: IStorageWrapper; + data: DataPublicPluginStart; + uiSettings: IUiSettingsClient; + application: ApplicationStart; + notifications: NotificationsStart; + navigation: NavigationPublicPluginStart; + attributeService: LensAttributeService; + savedObjectsClient: SavedObjectsStart['client']; + getOriginatingAppName: () => string | undefined; + + // Temporarily required until the 'by value' paradigm is default. + dashboardFeatureFlag: DashboardFeatureFlagConfig; +} + +export interface LensTopNavActions { + saveAndReturn: () => void; + showSaveModal: () => void; + cancel: () => void; +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/save.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/save.ts index 6da6d5a8c118fe..4cb523f128a8ce 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/save.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/save.ts @@ -59,7 +59,7 @@ export function getSavedObjectFormat({ return { doc: { - id: state.persistedId, + savedObjectId: state.persistedId, title: state.title, description: state.description, type: 'lens', diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts index c7f505aeca517b..80d007e17f711c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts @@ -376,7 +376,7 @@ describe('editor_frame state management', () => { { type: 'VISUALIZATION_LOADED', doc: { - id: 'b', + savedObjectId: 'b', state: { datasourceStates: { a: { foo: 'c' } }, visualization: { bar: 'd' }, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts index 09674ebf2ade2c..fc8daaed059ddf 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts @@ -156,7 +156,7 @@ export const reducer = (state: EditorFrameState, action: Action): EditorFrameSta case 'VISUALIZATION_LOADED': return { ...state, - persistedId: action.doc.id, + persistedId: action.doc.savedObjectId, title: action.doc.title, description: action.doc.description, datasourceStates: Object.entries(action.doc.state.datasourceStates).reduce( diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx index 1e2df28cad7b1a..d48f9ed713caff 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx @@ -5,12 +5,29 @@ */ import { Subject } from 'rxjs'; -import { Embeddable } from './embeddable'; +import { + Embeddable, + LensByValueInput, + LensByReferenceInput, + LensSavedObjectAttributes, + LensEmbeddableInput, +} from './embeddable'; import { ReactExpressionRendererProps } from 'src/plugins/expressions/public'; -import { Query, TimeRange, Filter, TimefilterContract } from 'src/plugins/data/public'; +import { + Query, + TimeRange, + Filter, + TimefilterContract, + IndexPatternsContract, +} from 'src/plugins/data/public'; import { Document } from '../../persistence'; import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public/embeddable'; +import { coreMock, httpServiceMock } from '../../../../../../src/core/public/mocks'; +import { IBasePath } from '../../../../../../src/core/public'; +import { AttributeService } from '../../../../../../src/plugins/dashboard/public'; +import { Ast } from '@kbn/interpreter/common'; +import { LensAttributeService } from '../../lens_attribute_service'; jest.mock('../../../../../../src/plugins/inspector/public/', () => ({ isAvailable: false, @@ -29,61 +46,95 @@ const savedVis: Document = { visualizationType: '', }; +const attributeServiceMockFromSavedVis = (document: Document): LensAttributeService => { + const core = coreMock.createStart(); + const service = new AttributeService< + LensSavedObjectAttributes, + LensByValueInput, + LensByReferenceInput + >( + 'lens', + jest.fn(), + core.savedObjects.client, + core.overlays, + core.i18n.Context, + core.notifications.toasts + ); + service.unwrapAttributes = jest.fn((input: LensByValueInput | LensByReferenceInput) => { + return Promise.resolve({ ...document } as LensSavedObjectAttributes); + }); + service.wrapAttributes = jest.fn(); + return service; +}; + describe('embeddable', () => { let mountpoint: HTMLDivElement; let expressionRenderer: jest.Mock; let getTrigger: jest.Mock; let trigger: { exec: jest.Mock }; + let basePath: IBasePath; + let attributeService: AttributeService< + LensSavedObjectAttributes, + LensByValueInput, + LensByReferenceInput + >; beforeEach(() => { mountpoint = document.createElement('div'); expressionRenderer = jest.fn((_props) => null); trigger = { exec: jest.fn() }; getTrigger = jest.fn(() => trigger); + attributeService = attributeServiceMockFromSavedVis(savedVis); + const http = httpServiceMock.createSetupContract({ basePath: '/test' }); + basePath = http.basePath; }); afterEach(() => { mountpoint.remove(); }); - it('should render expression with expression renderer', () => { + it('should render expression with expression renderer', async () => { const embeddable = new Embeddable( - dataPluginMock.createSetupContract().query.timefilter.timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123' } + {} as LensEmbeddableInput ); + await embeddable.initializeSavedVis({} as LensEmbeddableInput); embeddable.render(mountpoint); expect(expressionRenderer).toHaveBeenCalledTimes(1); expect(expressionRenderer.mock.calls[0][0]!.expression).toEqual('my | expression'); }); - it('should re-render if new input is pushed', () => { + it('should re-render if new input is pushed', async () => { const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; const query: Query = { language: 'kquery', query: '' }; const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }]; const embeddable = new Embeddable( - dataPluginMock.createSetupContract().query.timefilter.timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123' } + { id: '123' } as LensEmbeddableInput ); + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); embeddable.updateInput({ @@ -95,61 +146,74 @@ describe('embeddable', () => { expect(expressionRenderer).toHaveBeenCalledTimes(2); }); - it('should pass context to embeddable', () => { + it('should pass context to embeddable', async () => { const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; const query: Query = { language: 'kquery', query: '' }; const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }]; + const input = { savedObjectId: '123', timeRange, query, filters } as LensEmbeddableInput; + const embeddable = new Embeddable( - dataPluginMock.createSetupContract().query.timefilter.timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123', timeRange, query, filters } + input ); + await embeddable.initializeSavedVis(input); embeddable.render(mountpoint); - expect(expressionRenderer.mock.calls[0][0].searchContext).toEqual({ - timeRange, - query: [query, savedVis.state.query], - filters, - }); + expect(expressionRenderer.mock.calls[0][0].searchContext).toEqual( + expect.objectContaining({ + timeRange, + query: [query, savedVis.state.query], + filters, + }) + ); }); - it('should merge external context with query and filters of the saved object', () => { + it('should merge external context with query and filters of the saved object', async () => { const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; const query: Query = { language: 'kquery', query: 'external filter' }; const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }]; + const newSavedVis = { + ...savedVis, + state: { + ...savedVis.state, + query: { language: 'kquery', query: 'saved filter' }, + filters: [ + { meta: { alias: 'test', negate: false, disabled: false, indexRefName: 'filter-0' } }, + ], + }, + references: [{ type: 'index-pattern', name: 'filter-0', id: 'my-index-pattern-id' }], + }; + attributeService = attributeServiceMockFromSavedVis(newSavedVis); + + const input = { savedObjectId: '123', timeRange, query, filters } as LensEmbeddableInput; + const embeddable = new Embeddable( - dataPluginMock.createSetupContract().query.timefilter.timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis: { - ...savedVis, - state: { - ...savedVis.state, - query: { language: 'kquery', query: 'saved filter' }, - filters: [ - { meta: { alias: 'test', negate: false, disabled: false, indexRefName: 'filter-0' } }, - ], - }, - references: [{ type: 'index-pattern', name: 'filter-0', id: 'my-index-pattern-id' }], - }, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123', timeRange, query, filters } + input ); + await embeddable.initializeSavedVis(input); embeddable.render(mountpoint); expect(expressionRenderer.mock.calls[0][0].searchContext).toEqual({ @@ -163,20 +227,22 @@ describe('embeddable', () => { }); }); - it('should execute trigger on event from expression renderer', () => { + it('should execute trigger on event from expression renderer', async () => { const embeddable = new Embeddable( - dataPluginMock.createSetupContract().query.timefilter.timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123' } + { id: '123' } as LensEmbeddableInput ); + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); const onEvent = expressionRenderer.mock.calls[0][0].onEvent!; @@ -190,24 +256,31 @@ describe('embeddable', () => { ); }); - it('should not re-render if only change is in disabled filter', () => { + it('should not re-render if only change is in disabled filter', async () => { const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; const query: Query = { language: 'kquery', query: '' }; const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: true } }]; const embeddable = new Embeddable( - dataPluginMock.createSetupContract().query.timefilter.timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123', timeRange, query, filters } + { id: '123', timeRange, query, filters } as LensEmbeddableInput ); + await embeddable.initializeSavedVis({ + id: '123', + timeRange, + query, + filters, + } as LensEmbeddableInput); embeddable.render(mountpoint); embeddable.updateInput({ @@ -219,7 +292,7 @@ describe('embeddable', () => { expect(expressionRenderer).toHaveBeenCalledTimes(1); }); - it('should re-render on auto refresh fetch observable', () => { + it('should re-render on auto refresh fetch observable', async () => { const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; const query: Query = { language: 'kquery', query: '' }; const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: true } }]; @@ -230,18 +303,25 @@ describe('embeddable', () => { } as unknown) as TimefilterContract; const embeddable = new Embeddable( - timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123', timeRange, query, filters } + { id: '123', timeRange, query, filters } as LensEmbeddableInput ); + await embeddable.initializeSavedVis({ + id: '123', + timeRange, + query, + filters, + } as LensEmbeddableInput); embeddable.render(mountpoint); autoRefreshFetchSubject.next(); diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index 4df218a3e94e96..61a5d8cacdc4f3 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -13,10 +13,12 @@ import { Query, TimefilterContract, TimeRange, + IndexPattern, } from 'src/plugins/data/public'; import { ExecutionContextSearch } from 'src/plugins/expressions'; import { Subscription } from 'rxjs'; +import { Ast } from '@kbn/interpreter/common'; import { ExpressionRendererEvent, ReactExpressionRendererType, @@ -28,41 +30,56 @@ import { EmbeddableInput, EmbeddableOutput, IContainer, + SavedObjectEmbeddableInput, + ReferenceOrValueEmbeddable, } from '../../../../../../src/plugins/embeddable/public'; import { DOC_TYPE, Document, injectFilterReferences } from '../../persistence'; import { ExpressionWrapper } from './expression_wrapper'; import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; import { isLensBrushEvent, isLensFilterEvent } from '../../types'; -export interface LensEmbeddableConfiguration { - expression: string | null; - savedVis: Document; - editUrl: string; - editPath: string; - editable: boolean; - indexPatterns?: IIndexPattern[]; -} +import { IndexPatternsContract } from '../../../../../../src/plugins/data/public'; +import { getEditPath } from '../../../common'; +import { IBasePath } from '../../../../../../src/core/public'; +import { LensAttributeService } from '../../lens_attribute_service'; -export interface LensEmbeddableInput extends EmbeddableInput { - timeRange?: TimeRange; - query?: Query; - filters?: Filter[]; -} +export type LensSavedObjectAttributes = Omit; + +export type LensByValueInput = { + attributes: LensSavedObjectAttributes; +} & EmbeddableInput; + +export type LensByReferenceInput = SavedObjectEmbeddableInput & EmbeddableInput; +export type LensEmbeddableInput = LensByValueInput | LensByReferenceInput; export interface LensEmbeddableOutput extends EmbeddableOutput { indexPatterns?: IIndexPattern[]; } -export class Embeddable extends AbstractEmbeddable { +export interface LensEmbeddableDeps { + attributeService: LensAttributeService; + documentToExpression: (doc: Document) => Promise; + toExpressionString: (astObj: Ast, type?: string) => string; + editable: boolean; + indexPatternService: IndexPatternsContract; + expressionRenderer: ReactExpressionRendererType; + timefilter: TimefilterContract; + basePath: IBasePath; + getTrigger?: UiActionsStart['getTrigger'] | undefined; +} + +export class Embeddable + extends AbstractEmbeddable + implements ReferenceOrValueEmbeddable { type = DOC_TYPE; private expressionRenderer: ReactExpressionRendererType; - private getTrigger: UiActionsStart['getTrigger'] | undefined; - private expression: string | null; - private savedVis: Document; + private savedVis: Document | undefined; + private expression: string | undefined | null; private domNode: HTMLElement | Element | undefined; private subscription: Subscription; private autoRefreshFetchSubscription: Subscription; + private isInitialized = false; private externalSearchContext: { timeRange?: TimeRange; @@ -72,50 +89,32 @@ export class Embeddable extends AbstractEmbeddable this.onContainerStateChanged(initialInput)); this.subscription = this.getInput$().subscribe((input) => this.onContainerStateChanged(input)); - this.onContainerStateChanged(initialInput); - this.autoRefreshFetchSubscription = timefilter + this.autoRefreshFetchSubscription = deps.timefilter .getAutoRefreshFetch$() .subscribe(this.reload.bind(this)); } public supportedTriggers() { + if (!this.savedVis) { + return []; + } switch (this.savedVis.visualizationType) { case 'lnsXY': return [VIS_EVENT_TO_TRIGGER.filter, VIS_EVENT_TO_TRIGGER.brush]; @@ -128,6 +127,22 @@ export class Embeddable extends AbstractEmbeddable !filter.meta.disabled) @@ -144,9 +159,7 @@ export class Embeddable extends AbstractEmbeddable, @@ -173,6 +189,9 @@ export class Embeddable extends AbstractEmbeddable { - if (!this.getTrigger || this.input.disableTriggers) { + if (!this.deps.getTrigger || this.input.disableTriggers) { return; } if (isLensBrushEvent(event)) { - this.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ data: event.data, embeddable: this, }); } if (isLensFilterEvent(event)) { - this.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ data: event.data, embeddable: this, }); } }; - destroy() { - super.destroy(); - if (this.domNode) { - unmountComponentAtNode(this.domNode); - } - if (this.subscription) { - this.subscription.unsubscribe(); - } - this.autoRefreshFetchSubscription.unsubscribe(); - } - - reload() { + async reload() { const currentTime = Date.now(); if (this.externalSearchContext.lastReloadRequestTime !== currentTime) { this.externalSearchContext = { @@ -233,4 +241,68 @@ export class Embeddable extends AbstractEmbeddable type === 'index-pattern') + .map(async ({ id }) => { + try { + return await this.deps.indexPatternService.get(id); + } catch (error) { + // Unable to load index pattern, ignore error as the index patterns are only used to + // configure the filter and query bar - there is still a good chance to get the visualization + // to show. + return null; + } + }) + .filter((promise): promise is Promise => Boolean(promise)); + const indexPatterns = await Promise.all(promises); + // passing edit url and index patterns to the output of this embeddable for + // the container to pick them up and use them to configure filter bar and + // config dropdown correctly. + const input = this.getInput(); + const title = input.hidePanelTitles ? '' : input.title || this.savedVis.title; + const savedObjectId = (input as LensByReferenceInput).savedObjectId; + this.updateOutput({ + ...this.getOutput(), + defaultTitle: this.savedVis.title, + title, + editPath: getEditPath(savedObjectId), + editUrl: this.deps.basePath.prepend(`/app/lens${getEditPath(savedObjectId)}`), + indexPatterns, + }); + } + + public inputIsRefType = ( + input: LensByValueInput | LensByReferenceInput + ): input is LensByReferenceInput => { + return this.deps.attributeService.inputIsRefType(input); + }; + + public getInputAsRefType = async (): Promise => { + const input = this.deps.attributeService.getExplicitInputFromEmbeddable(this); + return this.deps.attributeService.getInputAsRefType(input, { + showSaveModal: true, + saveModalTitle: this.getTitle(), + }); + }; + + public getInputAsValueType = async (): Promise => { + const input = this.deps.attributeService.getExplicitInputFromEmbeddable(this); + return this.deps.attributeService.getInputAsValueType(input); + }; + + destroy() { + super.destroy(); + if (this.domNode) { + unmountComponentAtNode(this.domNode); + } + if (this.subscription) { + this.subscription.unsubscribe(); + } + this.autoRefreshFetchSubscription.unsubscribe(); + } } diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts index b8f9f8de1d2868..8771d1ebaddb1b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts @@ -4,33 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Capabilities, HttpSetup, SavedObjectsClientContract } from 'kibana/public'; +import { Capabilities, HttpSetup } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { RecursiveReadonly } from '@kbn/utility-types'; import { toExpression, Ast } from '@kbn/interpreter/target/common'; import { IndexPatternsContract, - IndexPattern, TimefilterContract, } from '../../../../../../src/plugins/data/public'; import { ReactExpressionRendererType } from '../../../../../../src/plugins/expressions/public'; import { EmbeddableFactoryDefinition, - ErrorEmbeddable, - EmbeddableInput, IContainer, } from '../../../../../../src/plugins/embeddable/public'; -import { Embeddable } from './embeddable'; -import { SavedObjectIndexStore, DOC_TYPE } from '../../persistence'; -import { getEditPath } from '../../../common'; +import { Embeddable, LensByReferenceInput, LensEmbeddableInput } from './embeddable'; +import { DOC_TYPE } from '../../persistence'; import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; import { Document } from '../../persistence/saved_object_store'; +import { LensAttributeService } from '../../lens_attribute_service'; -interface StartServices { +export interface LensEmbeddableStartServices { timefilter: TimefilterContract; coreHttp: HttpSetup; + attributeService: LensAttributeService; capabilities: RecursiveReadonly; - savedObjectsClient: SavedObjectsClientContract; expressionRenderer: ReactExpressionRendererType; indexPatternService: IndexPatternsContract; uiActions?: UiActionsStart; @@ -47,7 +44,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { getIconForSavedObject: () => 'lensApp', }; - constructor(private getStartServices: () => Promise) {} + constructor(private getStartServices: () => Promise) {} public isEditable = async () => { const { capabilities } = await this.getStartServices(); @@ -66,59 +63,40 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { createFromSavedObject = async ( savedObjectId: string, - input: Partial & { id: string }, + input: LensEmbeddableInput, parent?: IContainer ) => { + if (!(input as LensByReferenceInput).savedObjectId) { + (input as LensByReferenceInput).savedObjectId = savedObjectId; + } + return this.create(input, parent); + }; + + async create(input: LensEmbeddableInput, parent?: IContainer) { const { - savedObjectsClient, - coreHttp, - indexPatternService, timefilter, expressionRenderer, documentToExpression, uiActions, + coreHttp, + attributeService, + indexPatternService, } = await this.getStartServices(); - const store = new SavedObjectIndexStore(savedObjectsClient); - const savedVis = await store.load(savedObjectId); - - const promises = savedVis.references - .filter(({ type }) => type === 'index-pattern') - .map(async ({ id }) => { - try { - return await indexPatternService.get(id); - } catch (error) { - // Unable to load index pattern, ignore error as the index patterns are only used to - // configure the filter and query bar - there is still a good chance to get the visualization - // to show. - return null; - } - }); - const indexPatterns = ( - await Promise.all(promises) - ).filter((indexPattern: IndexPattern | null): indexPattern is IndexPattern => - Boolean(indexPattern) - ); - - const expression = await documentToExpression(savedVis); return new Embeddable( - timefilter, - expressionRenderer, - uiActions?.getTrigger, { - savedVis, - editPath: getEditPath(savedObjectId), - editUrl: coreHttp.basePath.prepend(`/app/lens${getEditPath(savedObjectId)}`), + attributeService, + indexPatternService, + timefilter, + expressionRenderer, editable: await this.isEditable(), - indexPatterns, - expression: expression ? toExpression(expression) : null, + basePath: coreHttp.basePath, + getTrigger: uiActions?.getTrigger, + documentToExpression, + toExpressionString: toExpression, }, input, parent ); - }; - - async create(input: EmbeddableInput) { - return new ErrorEmbeddable('Lens can only be created from a saved object', input); } } diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx index 7b1d091c1c8fe9..c1b6d74bb49c00 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx @@ -41,7 +41,8 @@ describe('editor_frame service', () => { (async () => { pluginInstance.setup( coreMock.createSetup() as CoreSetup, - pluginSetupDependencies + pluginSetupDependencies, + jest.fn() ); const publicAPI = pluginInstance.start(coreMock.createStart(), pluginStartDependencies); const instance = await publicAPI.createInstance(); @@ -61,7 +62,8 @@ describe('editor_frame service', () => { it('should not have child nodes after unmount', async () => { pluginInstance.setup( coreMock.createSetup() as CoreSetup, - pluginSetupDependencies + pluginSetupDependencies, + jest.fn() ); const publicAPI = pluginInstance.start(coreMock.createStart(), pluginStartDependencies); const instance = await publicAPI.createInstance(); diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.tsx index 5fc347179a032b..bebc3e69899026 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx @@ -25,10 +25,12 @@ import { Document } from '../persistence/saved_object_store'; import { EditorFrame } from './editor_frame'; import { mergeTables } from './merge_tables'; import { formatColumn } from './format_column'; -import { EmbeddableFactory } from './embeddable/embeddable_factory'; +import { EmbeddableFactory, LensEmbeddableStartServices } from './embeddable/embeddable_factory'; import { getActiveDatasourceIdFromDoc } from './editor_frame/state_management'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; +import { DashboardStart } from '../../../../../src/plugins/dashboard/public'; import { persistedStateToExpression } from './editor_frame/state_helpers'; +import { LensAttributeService } from '../lens_attribute_service'; export interface EditorFrameSetupPlugins { data: DataPublicPluginSetup; @@ -39,6 +41,7 @@ export interface EditorFrameSetupPlugins { export interface EditorFrameStartPlugins { data: DataPublicPluginStart; embeddable?: EmbeddableStart; + dashboard?: DashboardStart; expressions: ExpressionsStart; uiActions?: UiActionsStart; } @@ -78,16 +81,17 @@ export class EditorFrameService { public setup( core: CoreSetup, - plugins: EditorFrameSetupPlugins + plugins: EditorFrameSetupPlugins, + getAttributeService: () => LensAttributeService ): EditorFrameSetup { plugins.expressions.registerFunction(() => mergeTables); plugins.expressions.registerFunction(() => formatColumn); - const getStartServices = async () => { + const getStartServices = async (): Promise => { const [coreStart, deps] = await core.getStartServices(); return { + attributeService: getAttributeService(), capabilities: coreStart.application.capabilities, - savedObjectsClient: coreStart.savedObjects.client, coreHttp: coreStart.http, timefilter: deps.data.query.timefilter.timefilter, expressionRenderer: deps.expressions.ReactExpressionRenderer, diff --git a/x-pack/plugins/lens/public/lens_attribute_service.ts b/x-pack/plugins/lens/public/lens_attribute_service.ts new file mode 100644 index 00000000000000..3c43fd98cceb46 --- /dev/null +++ b/x-pack/plugins/lens/public/lens_attribute_service.ts @@ -0,0 +1,52 @@ +/* + * 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 { CoreStart } from '../../../../src/core/public'; +import { LensPluginStartDependencies } from './plugin'; +import { AttributeService } from '../../../../src/plugins/dashboard/public'; +import { + LensSavedObjectAttributes, + LensByValueInput, + LensByReferenceInput, +} from './editor_frame_service/embeddable/embeddable'; +import { SavedObjectIndexStore, DOC_TYPE } from './persistence'; + +export type LensAttributeService = AttributeService< + LensSavedObjectAttributes, + LensByValueInput, + LensByReferenceInput +>; + +export function getLensAttributeService( + core: CoreStart, + startDependencies: LensPluginStartDependencies +): LensAttributeService { + const savedObjectStore = new SavedObjectIndexStore(core.savedObjects.client); + return startDependencies.dashboard.getAttributeService< + LensSavedObjectAttributes, + LensByValueInput, + LensByReferenceInput + >(DOC_TYPE, { + customSaveMethod: async ( + type: string, + attributes: LensSavedObjectAttributes, + savedObjectId?: string + ) => { + const savedDoc = await savedObjectStore.save({ + ...attributes, + savedObjectId, + type: DOC_TYPE, + }); + return { id: savedDoc.savedObjectId }; + }, + customUnwrapMethod: (savedObject) => { + return { + ...savedObject.attributes, + references: savedObject.references, + }; + }, + }); +} diff --git a/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts b/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts index ba7c0ee6ae786e..6b6f81aeefed07 100644 --- a/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts +++ b/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts @@ -42,7 +42,7 @@ describe('LensStore', () => { }); expect(doc).toEqual({ - id: 'FOO', + savedObjectId: 'FOO', title: 'Hello', description: 'My doc', visualizationType: 'bar', @@ -82,7 +82,7 @@ describe('LensStore', () => { test('updates and returns a visualization document', async () => { const { client, store } = testStore(); const doc = await store.save({ - id: 'Gandalf', + savedObjectId: 'Gandalf', title: 'Even the very wise cannot see all ends.', visualizationType: 'line', references: [], @@ -95,7 +95,7 @@ describe('LensStore', () => { }); expect(doc).toEqual({ - id: 'Gandalf', + savedObjectId: 'Gandalf', title: 'Even the very wise cannot see all ends.', visualizationType: 'line', references: [], diff --git a/x-pack/plugins/lens/public/persistence/saved_object_store.ts b/x-pack/plugins/lens/public/persistence/saved_object_store.ts index e4609213ec7929..c6b3fd2cc0f652 100644 --- a/x-pack/plugins/lens/public/persistence/saved_object_store.ts +++ b/x-pack/plugins/lens/public/persistence/saved_object_store.ts @@ -13,7 +13,7 @@ import { Query } from '../../../../../src/plugins/data/public'; import { PersistableFilter } from '../../common'; export interface Document { - id?: string; + savedObjectId?: string; type?: string; visualizationType: string | null; title: string; @@ -30,11 +30,11 @@ export interface Document { export const DOC_TYPE = 'lens'; export interface DocumentSaver { - save: (vis: Document) => Promise<{ id: string }>; + save: (vis: Document) => Promise<{ savedObjectId: string }>; } export interface DocumentLoader { - load: (id: string) => Promise; + load: (savedObjectId: string) => Promise; } export type SavedObjectStore = DocumentLoader & DocumentSaver; @@ -46,20 +46,20 @@ export class SavedObjectIndexStore implements SavedObjectStore { this.client = client; } - async save(vis: Document) { - const { id, type, references, ...rest } = vis; + save = async (vis: Document) => { + const { savedObjectId, type, references, ...rest } = vis; // TODO: SavedObjectAttributes should support this kind of object, // remove this workaround when SavedObjectAttributes is updated. const attributes = (rest as unknown) as SavedObjectAttributes; - const result = await (id - ? this.safeUpdate(id, attributes, references) + const result = await (savedObjectId + ? this.safeUpdate(savedObjectId, attributes, references) : this.client.create(DOC_TYPE, attributes, { references, })); - return { ...vis, id: result.id }; - } + return { ...vis, savedObjectId: result.id }; + }; // As Lens is using an object to store its attributes, using the update API // will merge the new attribute object with the old one, not overwriting deleted @@ -68,7 +68,7 @@ export class SavedObjectIndexStore implements SavedObjectStore { // This function fixes this by doing two updates - one to empty out the document setting // every key to null, and a second one to load the new content. private async safeUpdate( - id: string, + savedObjectId: string, attributes: SavedObjectAttributes, references: SavedObjectReference[] ) { @@ -78,14 +78,14 @@ export class SavedObjectIndexStore implements SavedObjectStore { }); return ( await this.client.bulkUpdate([ - { type: DOC_TYPE, id, attributes: resetAttributes, references }, - { type: DOC_TYPE, id, attributes, references }, + { type: DOC_TYPE, id: savedObjectId, attributes: resetAttributes, references }, + { type: DOC_TYPE, id: savedObjectId, attributes, references }, ]) ).savedObjects[1]; } - async load(id: string): Promise { - const { type, attributes, references, error } = await this.client.get(DOC_TYPE, id); + async load(savedObjectId: string): Promise { + const { type, attributes, references, error } = await this.client.get(DOC_TYPE, savedObjectId); if (error) { throw error; @@ -94,7 +94,7 @@ export class SavedObjectIndexStore implements SavedObjectStore { return { ...(attributes as SavedObjectAttributes), references, - id, + savedObjectId, type, } as Document; } diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 60c7011d55300f..1655a571721f5b 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -7,6 +7,7 @@ import { AppMountParameters, CoreSetup, CoreStart } from 'kibana/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public'; import { EmbeddableSetup, EmbeddableStart } from 'src/plugins/embeddable/public'; +import { DashboardStart } from 'src/plugins/dashboard/public'; import { ExpressionsSetup, ExpressionsStart } from 'src/plugins/expressions/public'; import { VisualizationsSetup } from 'src/plugins/visualizations/public'; import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; @@ -35,6 +36,7 @@ import { getLensAliasConfig } from './vis_type_alias'; import { getSearchProvider } from './search_provider'; import './index.scss'; +import { getLensAttributeService, LensAttributeService } from './lens_attribute_service'; export interface LensPluginSetupDependencies { urlForwarding: UrlForwardingSetup; @@ -51,13 +53,14 @@ export interface LensPluginStartDependencies { expressions: ExpressionsStart; navigation: NavigationPublicPluginStart; uiActions: UiActionsStart; - embeddable: EmbeddableStart; + dashboard: DashboardStart; + embeddable?: EmbeddableStart; } - export class LensPlugin { private datatableVisualization: DatatableVisualization; private editorFrameService: EditorFrameService; private createEditorFrame: EditorFrameStart['createInstance'] | null = null; + private attributeService: LensAttributeService | null = null; private indexpatternDatasource: IndexPatternDatasource; private xyVisualization: XyVisualization; private metricVisualization: MetricVisualization; @@ -84,11 +87,15 @@ export class LensPlugin { globalSearch, }: LensPluginSetupDependencies ) { - const editorFrameSetupInterface = this.editorFrameService.setup(core, { - data, - embeddable, - expressions, - }); + const editorFrameSetupInterface = this.editorFrameService.setup( + core, + { + data, + embeddable, + expressions, + }, + () => this.attributeService! + ); const dependencies: IndexPatternDatasourceSetupPlugins & XyVisualizationPluginSetupPlugins & DatatableVisualizationPluginSetupPlugins & @@ -110,13 +117,22 @@ export class LensPlugin { visualizations.registerAlias(getLensAliasConfig()); + const getByValueFeatureFlag = async () => { + const [, deps] = await core.getStartServices(); + return deps.dashboard.dashboardFeatureFlagConfig; + }; + core.application.register({ id: 'lens', title: NOT_INTERNATIONALIZED_PRODUCT_NAME, navLinkStatus: AppNavLinkStatus.hidden, mount: async (params: AppMountParameters) => { const { mountApp } = await import('./app_plugin/mounter'); - return mountApp(core, params, this.createEditorFrame!); + return mountApp(core, params, { + createEditorFrame: this.createEditorFrame!, + attributeService: this.attributeService!, + getByValueFeatureFlag, + }); }, }); @@ -138,6 +154,7 @@ export class LensPlugin { } start(core: CoreStart, startDependencies: LensPluginStartDependencies) { + this.attributeService = getLensAttributeService(core, startDependencies); this.createEditorFrame = this.editorFrameService.start(core, startDependencies).createInstance; } diff --git a/x-pack/plugins/maps/public/routing/routes/maps_app/top_nav_config.tsx b/x-pack/plugins/maps/public/routing/routes/maps_app/top_nav_config.tsx index 47f41f2b76f3ef..8a0eb8db4d7aa0 100644 --- a/x-pack/plugins/maps/public/routing/routes/maps_app/top_nav_config.tsx +++ b/x-pack/plugins/maps/public/routing/routes/maps_app/top_nav_config.tsx @@ -67,17 +67,17 @@ export function getTopNavConfig({ savedMap.description = newDescription; savedMap.copyOnSave = newCopyOnSave; - let id; + let savedObjectId; try { savedMap.syncWithStore(); - id = await savedMap.save({ + savedObjectId = await savedMap.save({ confirmOverwrite: false, isTitleDuplicateConfirmed, onTitleDuplicate, }); // id not returned when save fails because of duplicate title check. // return and let user confirm duplicate title. - if (!id) { + if (!savedObjectId) { return {}; } } catch (err) { @@ -105,7 +105,7 @@ export function getTopNavConfig({ getCoreChrome().docTitle.change(savedMap.title); setBreadcrumbs(); - goToSpecifiedPath(`/map/${id}${window.location.hash}`); + goToSpecifiedPath(`/map/${savedObjectId}${window.location.hash}`); const newlyCreated = newCopyOnSave || isNewMap; if (newlyCreated && !returnToOrigin) { @@ -113,14 +113,14 @@ export function getTopNavConfig({ } else if (!!originatingApp && returnToOrigin) { if (newlyCreated && stateTransfer) { stateTransfer.navigateToWithEmbeddablePackage(originatingApp, { - state: { id, type: MAP_SAVED_OBJECT_TYPE }, + state: { input: { savedObjectId }, type: MAP_SAVED_OBJECT_TYPE }, }); } else { getNavigateToApp()(originatingApp); } } - return { id }; + return { id: savedObjectId }; } if (hasSaveAndReturnConfig) { From 298d5c1c9f86a4244f323b957bc370c04bbbfac7 Mon Sep 17 00:00:00 2001 From: gchaps <33642766+gchaps@users.noreply.github.com> Date: Wed, 23 Sep 2020 15:23:20 -0700 Subject: [PATCH 22/35] [DOCS] Removes duplicate entry from settings doc (#78343) --- docs/setup/settings.asciidoc | 63 +++++++++++++++++------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 7f48f21db71976..af68f3e5416286 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -54,7 +54,7 @@ overwritten by client-side headers, regardless of the |[[elasticsearch-hosts]] `elasticsearch.hosts:` | The URLs of the {es} instances to use for all your queries. All nodes listed here must be on the same cluster. *Default: `[ "http://localhost:9200" ]`* -+ + To enable SSL/TLS for outbound connections to {es}, use the `https` protocol in this setting. @@ -129,7 +129,7 @@ be set to `"required"` or `"optional"` to request a client certificate from [NOTE] ============ -These settings cannot be used in conjunction with +These settings cannot be used in conjunction with <>. ============ @@ -141,9 +141,9 @@ These settings cannot be used in conjunction with certificates, which make up a trusted certificate chain for {es}. This chain is used by {kib} to establish trust when making outbound SSL/TLS connections to {es}. -+ + In addition to this setting, trusted certificates may be specified via -<> and/or +<> and/or <>. | `elasticsearch.ssl.keyPassphrase:` @@ -157,7 +157,7 @@ corresponding private key. These are used by {kib} to authenticate itself when making outbound SSL/TLS connections to {es}. For this setting, you must also set the `xpack.security.http.ssl.client_authentication` setting in {es} to `"required"` or `"optional"` to request a client certificate from {kib}. -+ + If the keystore contains any additional certificates, they are used as a trusted certificate chain for {es}. This chain is used by {kib} to establish trust when making outbound SSL/TLS connections to {es}. In addition to this @@ -178,7 +178,7 @@ This setting cannot be used in conjunction with | `elasticsearch.ssl.keystore.password:` | The password that decrypts the keystore specified via -<>. If the keystore has no password, leave this +<>. If the keystore has no password, leave this as blank. If the keystore has an empty password, set this to `""`. @@ -187,14 +187,14 @@ as blank. If the keystore has an empty password, set this to authority (CA) certificates, which make up a trusted certificate chain for {es}. This chain is used by {kib} to establish trust when making outbound SSL/TLS connections to {es}. -+ + In addition to this setting, trusted certificates may be specified via <> and/or <>. |`elasticsearch.ssl.truststore.password:` | The password that decrypts the trust store specified via -<>. If the trust store +<>. If the trust store has no password, leave this as blank. If the trust store has an empty password, set this to `""`. | `elasticsearch.ssl.verificationMode:` @@ -300,15 +300,16 @@ the `polling` method could be used enabling that option. *Default: `false`* suppress all logging output. *Default: `false`* | `logging.timezone` - | Set to the canonical timezone ID -(for example, `America/Los_Angeles`) to log events using that timezone. For a -list of timezones, refer to https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. *Default: `UTC`* + | Set to the canonical time zone ID +(for example, `America/Los_Angeles`) to log events using that time zone. +For possible values, refer to +https://en.wikipedia.org/wiki/List_of_tz_database_time_zones[database time zones]. *Default: `UTC`* -| [[logging-verbose]] `logging.verbose:` {ece-icon} +| [[logging-verbose]] `logging.verbose:` {ess-icon} | Set to `true` to log all events, including system usage information and all requests. *Default: `false`* -| `map.includeElasticMapsService:` {ess-icon} +| [[regionmap-ES-map]] `map.includeElasticMapsService:` {ess-icon} | Set to `false` to disable connections to Elastic Maps Service. When `includeElasticMapsService` is turned off, only the vector layers configured by <> and the tile layer configured by <> are available in <>. *Default: `true`* @@ -317,7 +318,7 @@ and the tile layer configured by <> are availabl | Set to `true` to proxy all <> Elastic Maps Service requests through the {kib} server. *Default: `false`* -| [[regionmap-settings]] `map.regionmap:` {ess-icon} {ece-icon} +| [[regionmap-settings]] `map.regionmap:` {ess-icon} | Specifies additional vector layers for use in <> visualizations. Each layer object points to an external vector file that contains a geojson @@ -347,16 +348,10 @@ map.regionmap: [cols="2*<"] |=== -| [[regionmap-ES-map]] `map.includeElasticMapsService:` {ece-icon} - | Turns on or off whether layers from the Elastic Maps Service should be included in the vector -layer option list. By turning this off, -only the layers that are configured here will be included. The default is `true`. -This also affects whether tile-service from the Elastic Maps Service will be available. - -| [[regionmap-attribution]] `map.regionmap.layers[].attribution:` {ess-icon} {ece-icon} +| [[regionmap-attribution]] `map.regionmap.layers[].attribution:` {ess-icon} | Optional. References the originating source of the geojson file. -| [[regionmap-fields]] `map.regionmap.layers[].fields[]:` {ess-icon} {ece-icon} +| [[regionmap-fields]] `map.regionmap.layers[].fields[]:` {ess-icon} | Mandatory. Each layer can contain multiple fields to indicate what properties from the geojson features you wish to expose. The following shows how to define multiple @@ -382,11 +377,11 @@ map.regionmap: [cols="2*<"] |=== -| [[regionmap-field-description]] `map.regionmap.layers[].fields[].description:` {ess-icon} {ece-icon} +| [[regionmap-field-description]] `map.regionmap.layers[].fields[].description:` {ess-icon} | Mandatory. The human readable text that is shown under the Options tab when building the Region Map visualization. -| [[regionmap-field-name]] `map.regionmap.layers[].fields[].name:` {ess-icon} {ece-icon} +| [[regionmap-field-name]] `map.regionmap.layers[].fields[].name:` {ess-icon} | Mandatory. This value is used to do an inner-join between the document stored in {es} and the geojson file. For example, if the field in the geojson is @@ -394,30 +389,30 @@ called `Location` and has city names, there must be a field in {es} that holds the same values that {kib} can then use to lookup for the geoshape data. -| [[regionmap-name]] `map.regionmap.layers[].name:` {ess-icon} {ece-icon} +| [[regionmap-name]] `map.regionmap.layers[].name:` {ess-icon} | Mandatory. A description of the map being provided. -| [[regionmap-url]] `map.regionmap.layers[].url:` {ess-icon} {ece-icon} +| [[regionmap-url]] `map.regionmap.layers[].url:` {ess-icon} | Mandatory. The location of the geojson file as provided by a webserver. -| [[tilemap-settings]] `map.tilemap.options.attribution:` {ess-icon} {ece-icon} +| [[tilemap-settings]] `map.tilemap.options.attribution:` {ess-icon} | The map attribution string. *Default: `"© [Elastic Maps Service](https://www.elastic.co/elastic-maps-service)"`* -| [[tilemap-max-zoom]] `map.tilemap.options.maxZoom:` {ess-icon} {ece-icon} +| [[tilemap-max-zoom]] `map.tilemap.options.maxZoom:` {ess-icon} | The maximum zoom level. *Default: `10`* -| [[tilemap-min-zoom]] `map.tilemap.options.minZoom:` {ess-icon} {ece-icon} +| [[tilemap-min-zoom]] `map.tilemap.options.minZoom:` {ess-icon} | The minimum zoom level. *Default: `1`* -| [[tilemap-subdomains]] `map.tilemap.options.subdomains:` {ess-icon} {ece-icon} +| [[tilemap-subdomains]] `map.tilemap.options.subdomains:` {ess-icon} | An array of subdomains used by the tile service. Specify the position of the subdomain the URL with the token `{s}`. -| [[tilemap-url]] `map.tilemap.url:` {ess-icon} {ece-icon} +| [[tilemap-url]] `map.tilemap.url:` {ess-icon} | The URL to the tileservice that {kib} uses to display map tiles in tilemap visualizations. By default, {kib} reads this URL from an external metadata service, but users can @@ -521,7 +516,7 @@ These settings cannot be used in conjunction with <> and/or <>. | `server.ssl.cipherSuites:` @@ -549,7 +544,7 @@ is optional, as the key may not be encrypted. keystore contains any additional certificates, those will be used as a trusted certificate chain for {kib}. All of these are used by {kib} to establish trust when receiving inbound SSL/TLS connections from end users. The certificate chain is also used by {kib} to verify client certificates from end users when PKI authentication is enabled. -+ + In addition to this setting, trusted certificates may be specified via <> and/or <>. @@ -571,7 +566,7 @@ keystore has no password, leave this unset. If the keystore has an empty passwor | Path to a PKCS#12 trust store that contains one or more X.509 certificate authority (CA) certificates which make up a trusted certificate chain for {kib}. This chain is used by {kib} to establish trust when receiving inbound SSL/TLS connections from end users. If PKI authentication is enabled, this chain is also used by {kib} to verify client certificates from end users. -+ + In addition to this setting, trusted certificates may be specified via <> and/or <>. From 8ea5c575eb8cf1dc38649ea330eeddaacf9110fd Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Wed, 23 Sep 2020 16:31:34 -0700 Subject: [PATCH 23/35] [Metrics UI] Reduce the pagination size for snapshot request (#78051) Co-authored-by: Elastic Machine --- .../snapshot/lib/transform_request_to_metrics_api_request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/infra/server/routes/snapshot/lib/transform_request_to_metrics_api_request.ts b/x-pack/plugins/infra/server/routes/snapshot/lib/transform_request_to_metrics_api_request.ts index 814ec5e74ff339..ca64d832667a81 100644 --- a/x-pack/plugins/infra/server/routes/snapshot/lib/transform_request_to_metrics_api_request.ts +++ b/x-pack/plugins/infra/server/routes/snapshot/lib/transform_request_to_metrics_api_request.ts @@ -34,7 +34,7 @@ export const transformRequestToMetricsAPIRequest = async ( interval: timeRangeWithIntervalApplied.interval, }, metrics: transformSnapshotMetricsToMetricsAPIMetrics(snapshotRequest), - limit: snapshotRequest.overrideCompositeSize ? snapshotRequest.overrideCompositeSize : 10, + limit: snapshotRequest.overrideCompositeSize ? snapshotRequest.overrideCompositeSize : 5, alignDataToEnd: true, }; From ca27ec8385f547a3a0b87d7dc36067388207ceba Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Wed, 23 Sep 2020 16:32:24 -0700 Subject: [PATCH 24/35] [Metrics UI] Fix EC2 Query to only include aws.ec2 nodes (#78236) * [Metrics UI] Fix EC2 Query to only include aws.ec2 nodes * Making the filter more generic so we can apply it easily to any inventory model --- .../plugins/infra/common/inventory_models/aws_ec2/index.ts | 1 + x-pack/plugins/infra/common/inventory_models/types.ts | 1 + .../lib/transform_request_to_metrics_api_request.ts | 7 ++++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/infra/common/inventory_models/aws_ec2/index.ts b/x-pack/plugins/infra/common/inventory_models/aws_ec2/index.ts index c12137f7810d40..6453332be4f507 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_ec2/index.ts +++ b/x-pack/plugins/infra/common/inventory_models/aws_ec2/index.ts @@ -31,4 +31,5 @@ export const awsEC2: InventoryModel = { }, requiredMetrics: ['awsEC2CpuUtilization', 'awsEC2NetworkTraffic', 'awsEC2DiskIOBytes'], tooltipMetrics: ['cpu', 'rx', 'tx'], + nodeFilter: [{ term: { 'event.dataset': 'aws.ec2' } }], }; diff --git a/x-pack/plugins/infra/common/inventory_models/types.ts b/x-pack/plugins/infra/common/inventory_models/types.ts index 7eb74056dcf28f..5cc788f2383656 100644 --- a/x-pack/plugins/infra/common/inventory_models/types.ts +++ b/x-pack/plugins/infra/common/inventory_models/types.ts @@ -371,4 +371,5 @@ export interface InventoryModel { metrics: InventoryMetrics; requiredMetrics: InventoryMetric[]; tooltipMetrics: SnapshotMetricType[]; + nodeFilter?: object[]; } diff --git a/x-pack/plugins/infra/server/routes/snapshot/lib/transform_request_to_metrics_api_request.ts b/x-pack/plugins/infra/server/routes/snapshot/lib/transform_request_to_metrics_api_request.ts index ca64d832667a81..b18b45f4935d20 100644 --- a/x-pack/plugins/infra/server/routes/snapshot/lib/transform_request_to_metrics_api_request.ts +++ b/x-pack/plugins/infra/server/routes/snapshot/lib/transform_request_to_metrics_api_request.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { findInventoryFields } from '../../../../common/inventory_models'; +import { findInventoryFields, findInventoryModel } from '../../../../common/inventory_models'; import { MetricsAPIRequest, SnapshotRequest } from '../../../../common/http_api'; import { ESSearchClient } from '../../../lib/metrics/types'; import { InfraSource } from '../../../lib/sources'; @@ -52,6 +52,11 @@ export const transformRequestToMetricsAPIRequest = async ( filters.push({ term: { 'cloud.region': snapshotRequest.region } }); } + const inventoryModel = findInventoryModel(snapshotRequest.nodeType); + if (inventoryModel && inventoryModel.nodeFilter) { + inventoryModel.nodeFilter?.forEach((f) => filters.push(f)); + } + const inventoryFields = findInventoryFields( snapshotRequest.nodeType, source.configuration.fields From 441ebf65f77863de263b702666e9c5bdbe8cd2d1 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 23 Sep 2020 18:53:21 -0500 Subject: [PATCH 25/35] make QueryStringInput props explicit (#78336) --- .../kibana-plugin-plugins-data-public.md | 1 + ...in-plugins-data-public.querystringinput.md | 2 +- ...querystringinputprops.bubblesubmitevent.md | 11 +++++ ...-public.querystringinputprops.classname.md | 11 +++++ ...blic.querystringinputprops.datatestsubj.md | 11 +++++ ....querystringinputprops.disableautofocus.md | 11 +++++ ...lic.querystringinputprops.indexpatterns.md | 11 +++++ ...-public.querystringinputprops.isinvalid.md | 11 +++++ ...s.languageswitcherpopoveranchorposition.md | 11 +++++ ...ugins-data-public.querystringinputprops.md | 34 ++++++++++++++ ...ata-public.querystringinputprops.onblur.md | 11 +++++ ...a-public.querystringinputprops.onchange.md | 11 +++++ ...tringinputprops.onchangequeryinputfocus.md | 11 +++++ ...a-public.querystringinputprops.onsubmit.md | 11 +++++ ...blic.querystringinputprops.persistedlog.md | 11 +++++ ...ublic.querystringinputprops.placeholder.md | 11 +++++ ...ta-public.querystringinputprops.prepend.md | 11 +++++ ...data-public.querystringinputprops.query.md | 11 +++++ ...ublic.querystringinputprops.screentitle.md | 11 +++++ ...-data-public.querystringinputprops.size.md | 11 +++++ src/plugins/data/public/index.ts | 1 + src/plugins/data/public/public.api.md | 47 ++++++++++++++++++- src/plugins/data/public/ui/index.ts | 2 +- .../query_string_input/query_string_input.tsx | 9 ++-- 24 files changed, 276 insertions(+), 7 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.bubblesubmitevent.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.classname.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onblur.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchange.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onsubmit.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.persistedlog.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.placeholder.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.prepend.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.query.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.screentitle.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.size.md diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index accf46f534e89a..8625120d54848a 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -79,6 +79,7 @@ | [OptionedValueProp](./kibana-plugin-plugins-data-public.optionedvalueprop.md) | | | [QueryState](./kibana-plugin-plugins-data-public.querystate.md) | All query state service state | | [QueryStateChange](./kibana-plugin-plugins-data-public.querystatechange.md) | | +| [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) | | | [QuerySuggestionBasic](./kibana-plugin-plugins-data-public.querysuggestionbasic.md) | \* | | [QuerySuggestionField](./kibana-plugin-plugins-data-public.querysuggestionfield.md) | \* | | [QuerySuggestionGetFnArgs](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md) | \* | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md index e85747b8cc3d7d..aa7c3bb5d49323 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md @@ -7,5 +7,5 @@ Signature: ```typescript -QueryStringInput: React.FC> +QueryStringInput: React.FC ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.bubblesubmitevent.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.bubblesubmitevent.md new file mode 100644 index 00000000000000..5a41852001ac08 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.bubblesubmitevent.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [bubbleSubmitEvent](./kibana-plugin-plugins-data-public.querystringinputprops.bubblesubmitevent.md) + +## QueryStringInputProps.bubbleSubmitEvent property + +Signature: + +```typescript +bubbleSubmitEvent?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.classname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.classname.md new file mode 100644 index 00000000000000..7fa3b769771837 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.classname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [className](./kibana-plugin-plugins-data-public.querystringinputprops.classname.md) + +## QueryStringInputProps.className property + +Signature: + +```typescript +className?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md new file mode 100644 index 00000000000000..edaedf49f4b101 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [dataTestSubj](./kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md) + +## QueryStringInputProps.dataTestSubj property + +Signature: + +```typescript +dataTestSubj?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md new file mode 100644 index 00000000000000..cc4c6f606409e1 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [disableAutoFocus](./kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md) + +## QueryStringInputProps.disableAutoFocus property + +Signature: + +```typescript +disableAutoFocus?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md new file mode 100644 index 00000000000000..37831386960201 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [indexPatterns](./kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md) + +## QueryStringInputProps.indexPatterns property + +Signature: + +```typescript +indexPatterns: Array; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md new file mode 100644 index 00000000000000..a282ac3bc5049a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [isInvalid](./kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md) + +## QueryStringInputProps.isInvalid property + +Signature: + +```typescript +isInvalid?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md new file mode 100644 index 00000000000000..d133a0930b53d7 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [languageSwitcherPopoverAnchorPosition](./kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md) + +## QueryStringInputProps.languageSwitcherPopoverAnchorPosition property + +Signature: + +```typescript +languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md new file mode 100644 index 00000000000000..d503980da7947f --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md @@ -0,0 +1,34 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) + +## QueryStringInputProps interface + +Signature: + +```typescript +export interface QueryStringInputProps +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [bubbleSubmitEvent](./kibana-plugin-plugins-data-public.querystringinputprops.bubblesubmitevent.md) | boolean | | +| [className](./kibana-plugin-plugins-data-public.querystringinputprops.classname.md) | string | | +| [dataTestSubj](./kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md) | string | | +| [disableAutoFocus](./kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md) | boolean | | +| [indexPatterns](./kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md) | Array<IIndexPattern | string> | | +| [isInvalid](./kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md) | boolean | | +| [languageSwitcherPopoverAnchorPosition](./kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md) | PopoverAnchorPosition | | +| [onBlur](./kibana-plugin-plugins-data-public.querystringinputprops.onblur.md) | () => void | | +| [onChange](./kibana-plugin-plugins-data-public.querystringinputprops.onchange.md) | (query: Query) => void | | +| [onChangeQueryInputFocus](./kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md) | (isFocused: boolean) => void | | +| [onSubmit](./kibana-plugin-plugins-data-public.querystringinputprops.onsubmit.md) | (query: Query) => void | | +| [persistedLog](./kibana-plugin-plugins-data-public.querystringinputprops.persistedlog.md) | PersistedLog | | +| [placeholder](./kibana-plugin-plugins-data-public.querystringinputprops.placeholder.md) | string | | +| [prepend](./kibana-plugin-plugins-data-public.querystringinputprops.prepend.md) | any | | +| [query](./kibana-plugin-plugins-data-public.querystringinputprops.query.md) | Query | | +| [screenTitle](./kibana-plugin-plugins-data-public.querystringinputprops.screentitle.md) | string | | +| [size](./kibana-plugin-plugins-data-public.querystringinputprops.size.md) | SuggestionsListSize | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onblur.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onblur.md new file mode 100644 index 00000000000000..10f2ae2ea4f146 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onblur.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [onBlur](./kibana-plugin-plugins-data-public.querystringinputprops.onblur.md) + +## QueryStringInputProps.onBlur property + +Signature: + +```typescript +onBlur?: () => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchange.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchange.md new file mode 100644 index 00000000000000..fee44d7afd506f --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchange.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [onChange](./kibana-plugin-plugins-data-public.querystringinputprops.onchange.md) + +## QueryStringInputProps.onChange property + +Signature: + +```typescript +onChange?: (query: Query) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md new file mode 100644 index 00000000000000..0421ae9c8bac5d --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [onChangeQueryInputFocus](./kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md) + +## QueryStringInputProps.onChangeQueryInputFocus property + +Signature: + +```typescript +onChangeQueryInputFocus?: (isFocused: boolean) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onsubmit.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onsubmit.md new file mode 100644 index 00000000000000..951ec7419485ff --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onsubmit.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [onSubmit](./kibana-plugin-plugins-data-public.querystringinputprops.onsubmit.md) + +## QueryStringInputProps.onSubmit property + +Signature: + +```typescript +onSubmit?: (query: Query) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.persistedlog.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.persistedlog.md new file mode 100644 index 00000000000000..d1a8efb3640167 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.persistedlog.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [persistedLog](./kibana-plugin-plugins-data-public.querystringinputprops.persistedlog.md) + +## QueryStringInputProps.persistedLog property + +Signature: + +```typescript +persistedLog?: PersistedLog; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.placeholder.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.placeholder.md new file mode 100644 index 00000000000000..31e41f4d552053 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.placeholder.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [placeholder](./kibana-plugin-plugins-data-public.querystringinputprops.placeholder.md) + +## QueryStringInputProps.placeholder property + +Signature: + +```typescript +placeholder?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.prepend.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.prepend.md new file mode 100644 index 00000000000000..7be882058d3fdc --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.prepend.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [prepend](./kibana-plugin-plugins-data-public.querystringinputprops.prepend.md) + +## QueryStringInputProps.prepend property + +Signature: + +```typescript +prepend?: any; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.query.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.query.md new file mode 100644 index 00000000000000..f15f6d082332b5 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.query.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [query](./kibana-plugin-plugins-data-public.querystringinputprops.query.md) + +## QueryStringInputProps.query property + +Signature: + +```typescript +query: Query; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.screentitle.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.screentitle.md new file mode 100644 index 00000000000000..0c80252d74571b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.screentitle.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [screenTitle](./kibana-plugin-plugins-data-public.querystringinputprops.screentitle.md) + +## QueryStringInputProps.screenTitle property + +Signature: + +```typescript +screenTitle?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.size.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.size.md new file mode 100644 index 00000000000000..6b0e53a23e07be --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.size.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [size](./kibana-plugin-plugins-data-public.querystringinputprops.size.md) + +## QueryStringInputProps.size property + +Signature: + +```typescript +size?: SuggestionsListSize; +``` diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 57865f05871a1a..f7dceffa9fdbc3 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -420,6 +420,7 @@ export { StatefulSearchBarProps, FilterBar, QueryStringInput, + QueryStringInputProps, IndexPatternSelect, } from './ui'; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index ed58ee840a8f83..28dfbf824470c2 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1727,11 +1727,54 @@ export interface QueryStateChange extends QueryStateChangePartial { globalFilters?: boolean; } -// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "QueryStringInput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const QueryStringInput: React.FC>; +export const QueryStringInput: React.FC; + +// Warning: (ae-missing-release-tag) "QueryStringInputProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface QueryStringInputProps { + // (undocumented) + bubbleSubmitEvent?: boolean; + // (undocumented) + className?: string; + // (undocumented) + dataTestSubj?: string; + // (undocumented) + disableAutoFocus?: boolean; + // (undocumented) + indexPatterns: Array; + // (undocumented) + isInvalid?: boolean; + // (undocumented) + languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition; + // (undocumented) + onBlur?: () => void; + // (undocumented) + onChange?: (query: Query) => void; + // (undocumented) + onChangeQueryInputFocus?: (isFocused: boolean) => void; + // (undocumented) + onSubmit?: (query: Query) => void; + // Warning: (ae-forgotten-export) The symbol "PersistedLog" needs to be exported by the entry point index.d.ts + // + // (undocumented) + persistedLog?: PersistedLog; + // (undocumented) + placeholder?: string; + // (undocumented) + prepend?: any; + // (undocumented) + query: Query; + // (undocumented) + screenTitle?: string; + // Warning: (ae-forgotten-export) The symbol "SuggestionsListSize" needs to be exported by the entry point index.d.ts + // + // (undocumented) + size?: SuggestionsListSize; +} // @public (undocumented) export type QuerySuggestion = QuerySuggestionBasic | QuerySuggestionField; diff --git a/src/plugins/data/public/ui/index.ts b/src/plugins/data/public/ui/index.ts index 35b1bc50ddb1ef..299b9d2681578c 100644 --- a/src/plugins/data/public/ui/index.ts +++ b/src/plugins/data/public/ui/index.ts @@ -20,7 +20,7 @@ export { SuggestionsComponent } from './typeahead'; export { IndexPatternSelect } from './index_pattern_select'; export { FilterBar } from './filter_bar'; -export { QueryStringInput } from './query_string_input/query_string_input'; +export { QueryStringInput, QueryStringInputProps } from './query_string_input/query_string_input'; export { SearchBar, SearchBarProps, StatefulSearchBarProps } from './search_bar'; // @internal diff --git a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx index 8e1151b387fee7..0986ad0668c240 100644 --- a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx @@ -46,8 +46,7 @@ import { PersistedLog, getQueryLog, matchPairs, toUser, fromUser } from '../../q import { SuggestionsListSize } from '../typeahead/suggestions_component'; import { SuggestionsComponent } from '..'; -interface Props { - kibana: KibanaReactContextValue; +export interface QueryStringInputProps { indexPatterns: Array; query: Query; disableAutoFocus?: boolean; @@ -67,6 +66,10 @@ interface Props { isInvalid?: boolean; } +interface Props extends QueryStringInputProps { + kibana: KibanaReactContextValue; +} + interface State { isSuggestionsVisible: boolean; index: number | null; @@ -687,4 +690,4 @@ export class QueryStringInputUI extends Component { } } -export const QueryStringInput = withKibana(QueryStringInputUI); +export const QueryStringInput: React.FC = withKibana(QueryStringInputUI); From 34e8a3f139f6dd636345374238cee5e8b1d10351 Mon Sep 17 00:00:00 2001 From: Davis Plumlee <56367316+dplumlee@users.noreply.github.com> Date: Wed, 23 Sep 2020 18:13:15 -0600 Subject: [PATCH 26/35] [Security Solution] Changes rule details default stack-by value (#78357) --- .../pages/detection_engine/rules/details/index.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx index 4816358e06226d..ad8ab3ed3a1480 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx @@ -88,6 +88,7 @@ import { timelineDefaults } from '../../../../../timelines/store/timeline/defaul import { TimelineModel } from '../../../../../timelines/store/timeline/model'; import { useSourcererScope } from '../../../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../../../common/store/sourcerer/model'; +import { AlertsHistogramOption } from '../../../../components/alerts_histogram_panel/types'; enum RuleDetailTabs { alerts = 'alerts', @@ -345,6 +346,11 @@ export const RuleDetailsPageComponent: FC = ({ return null; } + const defaultRuleStackByOption: AlertsHistogramOption = { + text: 'event.category', + value: 'event.category', + }; + return ( <> {hasIndexWrite != null && !hasIndexWrite && } @@ -480,6 +486,7 @@ export const RuleDetailsPageComponent: FC = ({ signalIndexName={signalIndexName} setQuery={setQuery} stackByOptions={alertsHistogramOptions} + defaultStackByOption={defaultRuleStackByOption} to={to} updateDateRange={updateDateRangeCallback} /> From 66d11bd1c3c2ecdd2aad922be86f916a0d20c48f Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Wed, 23 Sep 2020 20:09:11 -0600 Subject: [PATCH 27/35] Optimize status lookup for plugins that have no custom statuses (#78342) --- src/core/server/status/plugins_status.test.ts | 8 +-- src/core/server/status/plugins_status.ts | 51 +++++++++++++++---- src/core/server/status/status_service.ts | 6 +-- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/core/server/status/plugins_status.test.ts b/src/core/server/status/plugins_status.test.ts index a75dc8c283698d..176e2414a8d04b 100644 --- a/src/core/server/status/plugins_status.test.ts +++ b/src/core/server/status/plugins_status.test.ts @@ -161,13 +161,13 @@ describe('PluginStatusService', () => { }, b: { level: ServiceStatusLevels.degraded, - summary: '[2] services are degraded', + summary: '[savedObjects]: savedObjects degraded', detail: 'See the status page for more information', meta: expect.any(Object), }, c: { level: ServiceStatusLevels.degraded, - summary: '[3] services are degraded', + summary: '[savedObjects]: savedObjects degraded', detail: 'See the status page for more information', meta: expect.any(Object), }, @@ -186,13 +186,13 @@ describe('PluginStatusService', () => { }, b: { level: ServiceStatusLevels.critical, - summary: '[2] services are critical', + summary: '[elasticsearch]: elasticsearch critical', detail: 'See the status page for more information', meta: expect.any(Object), }, c: { level: ServiceStatusLevels.critical, - summary: '[3] services are critical', + summary: '[elasticsearch]: elasticsearch critical', detail: 'See the status page for more information', meta: expect.any(Object), }, diff --git a/src/core/server/status/plugins_status.ts b/src/core/server/status/plugins_status.ts index 113d59b327c11f..988f2d9969ccbf 100644 --- a/src/core/server/status/plugins_status.ts +++ b/src/core/server/status/plugins_status.ts @@ -33,7 +33,17 @@ interface Deps { export class PluginsStatusService { private readonly pluginStatuses = new Map>(); private readonly update$ = new BehaviorSubject(true); - constructor(private readonly deps: Deps) {} + private readonly defaultInheritedStatus$: Observable; + + constructor(private readonly deps: Deps) { + this.defaultInheritedStatus$ = this.deps.core$.pipe( + map((coreStatus) => { + return getSummaryStatus(Object.entries(coreStatus), { + allAvailableSummary: `All dependencies are available`, + }); + }) + ); + } public set(plugin: PluginName, status$: Observable) { this.pluginStatuses.set(plugin, status$); @@ -57,14 +67,24 @@ export class PluginsStatusService { } public getDerivedStatus$(plugin: PluginName): Observable { - return combineLatest([this.deps.core$, this.getDependenciesStatus$(plugin)]).pipe( - map(([coreStatus, pluginStatuses]) => { - return getSummaryStatus( - [...Object.entries(coreStatus), ...Object.entries(pluginStatuses)], - { - allAvailableSummary: `All dependencies are available`, - } - ); + return this.update$.pipe( + switchMap(() => { + // Only go up the dependency tree if any of this plugin's dependencies have a custom status + // Helps eliminate memory overhead of creating thousands of Observables unnecessarily. + if (this.anyCustomStatuses(plugin)) { + return combineLatest([this.deps.core$, this.getDependenciesStatus$(plugin)]).pipe( + map(([coreStatus, pluginStatuses]) => { + return getSummaryStatus( + [...Object.entries(coreStatus), ...Object.entries(pluginStatuses)], + { + allAvailableSummary: `All dependencies are available`, + } + ); + }) + ); + } else { + return this.defaultInheritedStatus$; + } }) ); } @@ -95,4 +115,17 @@ export class PluginsStatusService { }) ); } + + /** + * Determines whether or not this plugin or any plugin in it's dependency tree have a custom status registered. + */ + private anyCustomStatuses(plugin: PluginName): boolean { + if (this.pluginStatuses.get(plugin)) { + return true; + } + + return this.deps.pluginDependencies + .get(plugin)! + .reduce((acc, depName) => acc || this.anyCustomStatuses(depName), false as boolean); + } } diff --git a/src/core/server/status/status_service.ts b/src/core/server/status/status_service.ts index 9acf93f2f81977..62f226405e81a1 100644 --- a/src/core/server/status/status_service.ts +++ b/src/core/server/status/status_service.ts @@ -70,10 +70,10 @@ export class StatusService implements CoreService { const core$ = this.setupCoreStatus({ elasticsearch, savedObjects }); this.pluginsStatus = new PluginsStatusService({ core$, pluginDependencies }); - const overall$: Observable = combineLatest( + const overall$: Observable = combineLatest([ core$, - this.pluginsStatus.getAll$() - ).pipe( + this.pluginsStatus.getAll$(), + ]).pipe( // Prevent many emissions at once from dependency status resolution from making this too noisy debounceTime(500), map(([coreStatus, pluginsStatus]) => { From 1f03ce41adbb43e6d1e4e98045efef3a72e74aa7 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 23 Sep 2020 20:51:07 -0700 Subject: [PATCH 28/35] [ci-metrics] add docs describing the metrics collected (#78363) Co-authored-by: spalger --- .../development-ci-metrics.asciidoc | 65 +++++++++++++++++++ docs/developer/contributing/index.asciidoc | 3 + 2 files changed, 68 insertions(+) create mode 100644 docs/developer/contributing/development-ci-metrics.asciidoc diff --git a/docs/developer/contributing/development-ci-metrics.asciidoc b/docs/developer/contributing/development-ci-metrics.asciidoc new file mode 100644 index 00000000000000..d4d54f1da7b8b6 --- /dev/null +++ b/docs/developer/contributing/development-ci-metrics.asciidoc @@ -0,0 +1,65 @@ +[[ci-metrics]] +== CI Metrics + +In addition to running our tests, CI collects metrics about the Kibana build. These metrics are sent to an external service to track changes over time, and to provide PR authors insights into the impact of their changes. + + +[[ci-metric-types]] +=== Metric types + + +[[ci-metric-types-bundle-size-metrics]] +==== Bundle size + +These metrics help contributors know how they are impacting the size of the bundles Kibana creates, and help make sure that Kibana loads as fast as possible. + +[[ci-metric-page-load-bundle-size]] `page load bundle size` :: +The size of the entry file produced for each bundle/plugin. This file is always loaded on every page load, so it should be as small as possible. To reduce this metric you can put any code that isn't necessary on every page load behind an https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports[`async import()`]. ++ +Code that is shared statically with other plugins will contribute to the `page load bundle size` of that plugin. This includes exports from the `public/index.ts` file and any file referenced by the `extraPublicDirs` manifest property. + +[[ci-metric-async-chunks-size]] `async chunks size` :: +An "async chunk" is created for the files imported by each https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports[`async import()`] statement. This metric tracks the sum size of these chunks, in bytes, broken down by plugin/bundle id. You can think of this as the amount of code users will have to download if they access all the components/applications within a bundle. + +[[ci-metric-misc-asset-size]] `miscellaneous assets size` :: +A "miscellaneous asset" is anything that isn't an async chunk or entry chunk, often images. This metric tracks the sum size of these assets, in bytes, broken down by plugin/bundle id. + +[[ci-metric-bundle-module-count]] `@kbn/optimizer bundle module count` :: +The number of separate modules included in each bundle/plugin. This is the best indicator we have for how long a specific bundle will take to be built by the `@kbn/optimizer`, so we report it to help people know when they've imported a module which might include a surprising number of sub-modules. + + +[[ci-metric-types-distributable-size]] +==== Distributable size + +The size of the Kibana distributable is an essential metric as it not only contributes to the time it takes to download, but it also impacts time it takes to extract the archive once downloaded. + +There are several metrics that we don't report on PRs because gzip-compression produces different file sizes even when provided the same input, so this metric would regularly show changes even though PR authors hadn't made any relevant changes. + +All metrics are collected from the `tar.gz` archive produced for the linux platform. + +[[ci-metric-distributable-file-count]] `distributable file count` :: +The number of files included in the default distributable. + +[[ci-metric-oss-distributable-file-count]] `oss distributable file count` :: +The number of files included in the OSS distributable. + +[[ci-metric-distributable-size]] `distributable size` :: +The size, in bytes, of the default distributable. _(not reported on PRs)_ + +[[ci-metric-oss-distributable-size]] `oss distributable size` :: +The size, in bytes, of the OSS distributable. _(not reported on PRs)_ + + +[[ci-metric-types-saved-object-field-counts]] +==== Saved Object field counts + +Elasticsearch limits the number of fields in an index to 1000 by default, and we want to avoid raising that limit. + +[[ci-metric-saved-object-field-count]] `Saved Objects .kibana field count` :: +The number of saved object fields broken down by saved object type. + + +[[ci-metric-adding-new-metrics]] +=== Adding new metrics + +You can report new metrics by using the `CiStatsReporter` class provided by the `@kbn/dev-utils` package. This class is automatically configured on CI and its methods noop when running outside of CI. For more details checkout the {kib-repo}blob/{branch}/packages/kbn-dev-utils/src/ci_stats_reporter[`CiStatsReporter` readme]. \ No newline at end of file diff --git a/docs/developer/contributing/index.asciidoc b/docs/developer/contributing/index.asciidoc index 99ab83bc2f073e..ecb37ffe9c97bf 100644 --- a/docs/developer/contributing/index.asciidoc +++ b/docs/developer/contributing/index.asciidoc @@ -9,6 +9,7 @@ Read <> to get your environment up and running, the * <> * <> * <> +* <> * <> * <> * <> @@ -78,6 +79,8 @@ include::development-tests.asciidoc[leveloffset=+1] include::interpreting-ci-failures.asciidoc[leveloffset=+1] +include::development-ci-metrics.asciidoc[leveloffset=+1] + include::development-documentation.asciidoc[leveloffset=+1] include::development-pull-request.asciidoc[leveloffset=+1] From 6a04a6410a37ea720bcad49df074a7ed9586ce46 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Wed, 23 Sep 2020 22:55:02 -0500 Subject: [PATCH 29/35] [Enterprise Search] Update Product Selector and add Setup Guide (#78233) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add conditional button text - Only shows error connectign if host is set - Removes conditional rendering of cards - Changes the action text from “Launch” to “Setup” * Add setup guide * Extract ProductSelector to component * Update index and add routes * Change setup guide text * Fix imports * Add missing mock * Update x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx Co-authored-by: Jason Stoltzfus * Remove Literals Co-authored-by: Constance * Remove Literals II - The Force Awakens Co-authored-by: Constance * Add back access checks * Remove hard-coded props 🤦🏼‍♂️ * Remove data-test-subj attr * Reafactor access check variables * Remove unused beforeEach Co-authored-by: Constance * Add newline Co-authored-by: Constance * Update image to compressed * Remove unused things * Update to new way of using lodash things 🤷🏽‍♀️ Co-authored-by: Jason Stoltzfus Co-authored-by: Constance --- .../product_card/product_card.test.tsx | 19 +++- .../components/product_card/product_card.tsx | 32 ++++-- .../components/product_selector/index.ts | 7 ++ .../product_selector.test.tsx | 54 ++++++++++ .../product_selector/product_selector.tsx | 97 ++++++++++++++++++ .../setup_guide/assets/getting_started.png | Bin 0 -> 194538 bytes .../components/setup_guide/index.ts | 7 ++ .../setup_guide/setup_guide.test.tsx | 21 ++++ .../components/setup_guide/setup_guide.tsx | 62 +++++++++++ .../enterprise_search/index.test.tsx | 49 +++------ .../applications/enterprise_search/index.tsx | 84 ++++----------- .../applications/enterprise_search/routes.ts | 8 ++ 12 files changed, 330 insertions(+), 110 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/index.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/assets/getting_started.png create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/index.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search/routes.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx index f651511e61b440..35301af44b413d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx @@ -5,8 +5,9 @@ */ import '../../../__mocks__/kea.mock'; +import '../../../__mocks__/shallow_usecontext.mock'; -import React from 'react'; +import React, { useContext } from 'react'; import { shallow } from 'enzyme'; import { EuiCard } from '@elastic/eui'; @@ -26,6 +27,7 @@ describe('ProductCard', () => { }); it('renders an App Search card', () => { + (useContext as jest.Mock).mockImplementationOnce(() => ({ config: { host: 'localhost' } })); const wrapper = shallow(); const card = wrapper.find(EuiCard).dive().shallow(); @@ -34,13 +36,14 @@ describe('ProductCard', () => { const button = card.find(EuiButton); expect(button.prop('to')).toEqual('/app/enterprise_search/app_search'); - expect(button.prop('data-test-subj')).toEqual('LaunchAppSearchButton'); + expect(button.prop('children')).toEqual('Launch App Search'); button.simulate('click'); expect(sendTelemetry).toHaveBeenCalledWith(expect.objectContaining({ metric: 'app_search' })); }); it('renders a Workplace Search card', () => { + (useContext as jest.Mock).mockImplementationOnce(() => ({ config: { host: 'localhost' } })); const wrapper = shallow(); const card = wrapper.find(EuiCard).dive().shallow(); @@ -49,11 +52,21 @@ describe('ProductCard', () => { const button = card.find(EuiButton); expect(button.prop('to')).toEqual('/app/enterprise_search/workplace_search'); - expect(button.prop('data-test-subj')).toEqual('LaunchWorkplaceSearchButton'); + expect(button.prop('children')).toEqual('Launch Workplace Search'); button.simulate('click'); expect(sendTelemetry).toHaveBeenCalledWith( expect.objectContaining({ metric: 'workplace_search' }) ); }); + + it('renders correct button text when host not present', () => { + (useContext as jest.Mock).mockImplementation(() => ({ config: { host: '' } })); + + const wrapper = shallow(); + const card = wrapper.find(EuiCard).dive().shallow(); + const button = card.find(EuiButton); + + expect(button.prop('children')).toEqual('Setup Workplace Search'); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx index 833a782a32f008..482d68736af013 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx @@ -4,13 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useContext } from 'react'; import { useValues } from 'kea'; -import upperFirst from 'lodash/upperFirst'; -import snakeCase from 'lodash/snakeCase'; +import { snakeCase } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiCard, EuiTextColor } from '@elastic/eui'; +import { KibanaContext, IKibanaContext } from '../../../index'; + import { EuiButton } from '../../../shared/react_router_helpers'; import { sendTelemetry } from '../../../shared/telemetry'; import { HttpLogic } from '../../../shared/http'; @@ -30,6 +31,25 @@ interface IProductCard { export const ProductCard: React.FC = ({ product, image }) => { const { http } = useValues(HttpLogic); + const { + config: { host }, + } = useContext(KibanaContext) as IKibanaContext; + + const LAUNCH_BUTTON_TEXT = i18n.translate( + 'xpack.enterpriseSearch.overview.productCard.launchButton', + { + defaultMessage: 'Launch {productName}', + values: { productName: product.NAME }, + } + ); + + const SETUP_BUTTON_TEXT = i18n.translate( + 'xpack.enterpriseSearch.overview.productCard.setupButton', + { + defaultMessage: 'Setup {productName}', + values: { productName: product.NAME }, + } + ); return ( = ({ product, image }) => { metric: snakeCase(product.ID), }) } - data-test-subj={`Launch${upperFirst(product.ID)}Button`} > - {i18n.translate('xpack.enterpriseSearch.overview.productCard.button', { - defaultMessage: `Launch {productName}`, - values: { productName: product.NAME }, - })} + {host ? LAUNCH_BUTTON_TEXT : SETUP_BUTTON_TEXT} } /> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/index.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/index.ts new file mode 100644 index 00000000000000..b67d130cd68f0d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { ProductSelector } from './product_selector'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.test.tsx new file mode 100644 index 00000000000000..44efa57db897ff --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.test.tsx @@ -0,0 +1,54 @@ +/* + * 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 '../../../__mocks__/shallow_usecontext.mock'; + +import React, { useContext } from 'react'; +import { shallow } from 'enzyme'; +import { EuiPage } from '@elastic/eui'; + +import { ProductSelector } from './'; +import { ProductCard } from '../product_card'; + +describe('ProductSelector', () => { + it('renders the overview page and product cards with no host set', () => { + (useContext as jest.Mock).mockImplementationOnce(() => ({ config: { host: '' } })); + const wrapper = shallow(); + + expect(wrapper.find(EuiPage).hasClass('enterpriseSearchOverview')).toBe(true); + expect(wrapper.find(ProductCard)).toHaveLength(2); + }); + + describe('access checks when host is set', () => { + beforeEach(() => { + (useContext as jest.Mock).mockImplementationOnce(() => ({ config: { host: 'localhost' } })); + }); + + it('does not render the App Search card if the user does not have access to AS', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(ProductCard)).toHaveLength(1); + expect(wrapper.find(ProductCard).prop('product').ID).toEqual('workplaceSearch'); + }); + + it('does not render the Workplace Search card if the user does not have access to WS', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(ProductCard)).toHaveLength(1); + expect(wrapper.find(ProductCard).prop('product').ID).toEqual('appSearch'); + }); + + it('does not render any cards if the user does not have access', () => { + const wrapper = shallow(); + + expect(wrapper.find(ProductCard)).toHaveLength(0); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx new file mode 100644 index 00000000000000..07b8d4b9926d7f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx @@ -0,0 +1,97 @@ +/* + * 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. + */ +/* + * 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, { useContext } from 'react'; + +import { + EuiPage, + EuiPageBody, + EuiPageHeader, + EuiPageHeaderSection, + EuiPageContentBody, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { KibanaContext, IKibanaContext } from '../../../index'; + +import { APP_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN } from '../../../../../common/constants'; + +import { SetEnterpriseSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; +import { SendEnterpriseSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; + +import { ProductCard } from '../product_card'; + +import AppSearchImage from '../../assets/app_search.png'; +import WorkplaceSearchImage from '../../assets/workplace_search.png'; + +interface IProductSelectorProps { + access: { + hasAppSearchAccess?: boolean; + hasWorkplaceSearchAccess?: boolean; + }; +} + +export const ProductSelector: React.FC = ({ access }) => { + const { hasAppSearchAccess, hasWorkplaceSearchAccess } = access; + const { + config: { host }, + } = useContext(KibanaContext) as IKibanaContext; + + const shouldShowAppSearchCard = !host || hasAppSearchAccess; + const shouldShowWorkplaceSearchCard = !host || hasWorkplaceSearchAccess; + + return ( + + + + + + + + +

+ {i18n.translate('xpack.enterpriseSearch.overview.heading', { + defaultMessage: 'Welcome to Elastic Enterprise Search', + })} +

+
+ +

+ {i18n.translate('xpack.enterpriseSearch.overview.subheading', { + defaultMessage: 'Select a product to get started', + })} +

+
+
+
+ + + {shouldShowAppSearchCard && ( + + + + )} + {shouldShowWorkplaceSearchCard && ( + + + + )} + + + +
+
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/assets/getting_started.png b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/assets/getting_started.png new file mode 100644 index 0000000000000000000000000000000000000000..f0fcb432c29e1b5b9f3b66fd58e0a65784798180 GIT binary patch literal 194538 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�ql5g>VD>@gEGXo+_AivS3P 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

;ShZ*E4sz=OqW`L|zxSiAsU=%j9`OP%xh2@$@ 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

  • qpq6nq)jhEg(+11qUe zhRk{)IK{$9@LCeGFFmdP&PJnLZY=hfCl)a)BLZlc@xfpplsc4JT(2`MQ)Zav&IjL_ z^goUO8Z-5++QRDULTz?ytEi%DJT7iMc@TVXe=O%y+2UA^#4ORXYXv6 z8~t~Bi4kt5(E6!fNX@N@o%e`x}kzN`hCV+K4O; z7s~T1E0x>`3LnE1Bg5u_SGE@y8@Jc?StKU=DC2Mb;F{?OAm(-UcM8?{`D$UD{+~lm zGZ-_Ry$9&O|?kn;DlA0qN~wC`PyqJOp<_&+0o*b4W{_uaq$zW1#9)m~siQQh6W zrE6IwKfBOCqOYc*5Bn|Pov_wJQOqj4((c(g_d zLb--s*=PjI1DPcEpQcCQbtV3W4#{PJr+L#z%8I5PvJf|M~$SENqxV^{v@jn($g8AE>EdbGaf@G|>)I(X?{`JY~m!&9^9jB7Bc3f3{9y zIFJ(fzwuN+gL&gy-m?0Zk>`Ht_n*PkDSPpGn2TFlGKv@e5qd2k{C0JCH z3g#BTLT*3ID~j0{sNG&u+UYL&EV7C>A*j26vq4Otx0YJ%@jxHUUli<&4r zz;j{Jc>ntKt*D?->A+P12ckYZi?*wS9OTx}W8+c;C?>V5Gz0R%;h=$- z!U0ODxW{4K*HL)Aw8L;v*(_D`DSlxb)ey?Ut9j=~<<|y#Spprj1z&5Ci=Gh=pY10TqxNZ^k@{=YCAqLBE{ zClHW!S*(RnIbBJgbU0<~J&NrIoPYzh*Q8vb)or4uY09a@8QEnbW`wqIQH}G;;$+p~ z4g=sc4R1Tj4-S)Nuol%-gamK@mna;F<6}}Sh7H9#RF4?dPdk(WgO#v$7PTl6pc)9{ zzThK*MAO*+dFKvQ-cD|EwDz+=<#FqZUb#aLy?zQERys%o-E1<+9H>d>pWOhJ-<_#RGn#E$8ML9aU zMn!%t-6_-LXKXUB9rqf zOn;}@U1);iJ8)@?19WK^C5i`sEQbUUGKT=ojtV*w#cEa6yv-J;v;8xY{Xv1l^S2EJ zXKOO#N39WLD@^p6-yQk?Y=*t?@Ra~U0jc8-qF+5V{Ug%aoppmWm6m-?tvop2lCjsPBt zs+_~6iB4<&gT_{=9!;rpzC2354mW{!ZUgOgN5Iu^rkxf<9m%g0)k8f3Wj5#A;GG@2 zBU>JwGCpK6a_^Tmig9;r-Pj0d0;5c=UBB(r42)*Y5_(&5W zpH~BcCpRvX7ANqfPk0 zLlp4vYm5WYd6=8=Xm6w5I^BY%80}7&tv>#mCX0(50emK_L`zw_V_~W!>{yBp6e^_j zF>~@@{G-3e%A^T$4Rytn2;k{MLp?w_PwiY6nqlVe^|I~4QGd$${nlK!u0Zi)i`3$Gs>Ng%58balV)1r`5%OyZ3 zF^Y3D*CmocN5j@p0rg{S)#7FyCNbng^@TFvo&Q5opMN+`9?Euk)|0YK+En6 zqGxib6o#WdpXrE|NUDJs+b2Y4v1T*`6t!{=90A0U&T+P@sGI0(xS=Bg$hK-tKEACr zn$Gj7Crtq)a@s)WH#JJN(ECs1(AmU%p8TkL5`4wEv9U_;)==2Fe`{zf(Ywav#wsdg zYc(w$@2wOviMqR)7@aF|(z#5fvc9)9B+j~8VXPOCYRbRH z(Fr_#7%WcUll^#z#Q*9C-uqqxp)bIwf!foe3yJlImEN3>I%20_)P1?$%Gg*Y7u5}+ zM2lJRz2$4m!>i=Vi!O4T6OJ4!tPSTNPD)k6_*0+VT|*-AB)mAzk{B?+qsKFNi|3N) zm2eMnK4r3=s4S0tay(7_Z4T_4wG8@2W?ayF%`}$Q?E3n0VSO$w{al$DjkEg>gXd3K zRc1x6_f{%vydjOJI%epUUK(nCM%Xf=bD8&yjU{G!Gzz~Qp@r+aYn8%!0v+&lNj}m= zOo~H5AMyC^%)sPQR~A_7SreZl7$$I&W996UC!1=FC3zDnp-OJKob_ap%Bff?I0$mB9Ui;W!vn!AY@!gC zfLo*?nJK4Io~h}{(yXN^p#a}-<}0NJROX=5Ja@6l)&%)?DJ3Znn3IXdDY8cAAQd%1_GadL1Q+ghiP1IH<(bP$-)D^np)XtF<(9MLq1l{7P6Sj1~MohoMt zvy;TNC>)$%7YeS-rQMo17JS{SDY@prT$*D^a?P2&ZB*`AdT=f7JMh4UGX2vi%$Ao| z#Wi5}M^Xt&LMYX^R#PD_IW88aL8-Q8KA z8E%m@-E@k4IrjdgrAfBPusslKxEf4BQ6>jSQXi2iPz)WRO)4Pb1;cO7&3jxhz{uyv zeUc$A48rbWEO1qJMmG|s+OXp89t#qDM(}tep%d{)MIM`UwaI?<=@lxVae|-e|5gAY z0kRZClx3&kS#5^ z4aL994P?7qfGKrN27#rN4>5eTTVUsRps$6n>sM#U2-0P|5dwO+1F!o8Hn^#YW8Yla z(*~SMdjQ++eRMs!U^)}r5Fl$HRURi`i{LwKb&b>* zu75EWN=uQOZ2=q!e7c`P0q-%#{VHCPkC)!_7PoHCDnRcNn;)#GePyqvOanvB<40i*)w3o3#m$NQN)0GI5DOb>2Jz>5tx6SyESi(vU= zOGQH#fci3Q(Ir|}YWW15d~#a}p1A92#TDB@n0bG~ZHRc=j0qRm$w0v;({0`;NuJgKEVpu z6u>vVeCd5}dF8VuC*0iIJ1+rMj<+ko9kKKw`~ZS@NM$Bgd9u>2oPcCus)N0+vvVoC zxn?3drC;{&M&3*I3S1DdWCYXVULEKQ1lnC-oWM6rm@>p>=Bi-C;A~J{fG*ZG8Np60 zVk^H_So8Vn0_q{p6F@rj=uV1Vc3A=FA=7j~07w9(DkX)`gy`~=i`)Z9od>Rt+H?%a z3;8xzu#?&KU|2eaEcx^Mhf}fTk>|M?sN*>BI7?kFqU7N!<*T_KKVhyWXoT&p;9Flr zlR(t(;lU^9S-yuc57-81{M6vgqj*V4h!q78H3ssVdz@ro%b?7>FHl^;LhK zg1;4iL!tgzQ?g$sHgUI|;3go0Ky16lxh${{@T`Pp2BubOunxf>BP$gNu#A9(>9Mr9 z)yc{t|5T%09{=Z2MWE{&vyON^AzVp79LJ z(rPQAi&tNS7YZnk{6x+;i3iy7e=LAhz@I&y0{--gRX~9uyy@jHdlQM!&!Yfdh?bT* zF9B5v8F1x;2{-*E|3cZr-h=n`qKEWNg8}edFoGf|AoONxrQ0t+&fy>uRiLN9;R01T z>E~P{I^bvk=s5;!`P3btmQk9nq&ycp6-_3PS|=x8C%V(q)&^1>#BMZ|iSpG1q;3*S zX9_J+=uAig5Au}y0;rvxzzcVQ@~N4#{gDnYlmn%p{H;Du=Zn3&i9nI;=_{8ymr5Y_ zhZ#i;&2+!zJxkg9t8ZC|$~G3=Ka!aQ-~ixc?!3QSNs|TRj}C?eg40Gw7g16tO!pg4 zL63Nwot}GpkK&Z%Cdj#m;sSjTxwj6{4Ex=~gm0q_M4Co|>z25Ry;*?A(BEOu2E?X@ zE=WZ0#W#RjakVY5v3XsG0Y^3pyp6Nq7_3tO{Y4OBguMs7 z@0_eix?p{_ANiPUa-%CI2|RQ+u&n_v05S3#Ol9y0Pq5WRD?Yq2>ZJg7Ub*TW?XPjw z0Uq`Al}i*rLjL=1CH)k@1$c$+j6Kl$t`Nv)IMfg9*%EV7;b-x}Bep1CJ&B3XMJ5^H z2W&K$!Q*IYITyf3n+kcn$>1+Q0iXV4TGIo>9}uCR%{X+SjPE)W&_yCAgozykL}Gd+ zgtD~>l213l&FW2gI@ z;_FEEz>}2#_HARxj_jlDJUiQ^7l9u*njfe$E3SWG-UIn%PX~Q!YTc*dlI{dF^|=&E zq!0-O_b}W~!dB`gB04q{Lq>q~v;SIm?s@_$9|@xMa@^i^ymfniehKY<`}_(Zb5KB? zkbd@YC4e_>6?L{K*P!yK1h6o}xhehnn?1s3C27$U zD=^%3kBkTV+W4j6bM=JAh%J01S8zBHD&k^|tn5fUB0Jf9E=4xm3Z)*Cv06zL;Fnqvwon8T- z*eDnRNFXW&5HT<&f-n2yWrzS0a4-t;0*NnR^5K+=eSiQ{bigqm;lsW$f&)1&$c{0Q z@QDCcZh)qQZ+II8u%QGnJxF@JXyrxZFT?9j0bB`D{6uWM5J2X%&};p9@730;WYD*H zM{gAc{Fa+v0)ZY;B)l_EY#BkU>1U#ft7v8`1fYHN#-6D$)Xq!=Fm@)^els=a=8$Ie z4ia*T077ZeOw$s+$@R?}A!Z)wh<$LE0*C;*SV9kKqX4!e1OugxjO5w=yvxggl$|hk zEv+dc=OdbCuHOhS`y*h89kGYj1wH(fF5^60Mz}EmJ&*PU*L~cW6HYYnT+rch6(m4f zGbAr??<7NDT-%F4@NUAC^nICWsMU)YLwoU~#Qm2w5V8u)$p^#vE?}bdFjEo?j0TWu zBLD}m)s??h!}I5`ga-n637?fWw7T#+qi%p_L330?9Y|GmqY`Bb=8?oAGPu#FL>S#Q z-RmY#@KH_j8FWCYu5`3tdQp35g`>*4y9kt|%Y|xQ^rGi_N2^IUNo>jf4ZJ(#abpN_ zvgGrtuM&O~f@>v56q*TFab&!~oKzfDLHhyaemo-d|CInf{pm^pPb`7&deRlp&I`=U zz)Q;`*bsN&dGR2+1XCJ8GCJS^x|u|F93BWi_g9%&EQ_H%_OTls_*i%M?I5 zU-Xt9#VANOLhX`b3b`jUyz!?xGda-LhTG?uU4(2^da*sVZ4tnWLQq~z=oCOVJ`A0t zZwo!Ue^xH7B{*2?l_=JgEhRM_%FZbiFBF^~D`;JkF|3gu!>@Psxu~WUNc@L0nq*Zk z#qdB|X+YNwyET3oW(J{FFc*W+;Eo|Mh}udYzlLm(Cxk(flk5lyyFs3gT=dG2CVuBa zx2b5d82?4Z{Q7!B@H9~kq;dn53(wz6ZmyewezFQxWbIHp&!P(cpSQXl>*9ETkaPQI z-TJuzo+t|DOvtA%Mt>A@F>RZu2z+{x=w)3qkY5ZcAbt&8nm# zUL=pBwWT<^-D32lk?Dx&Q^+s!#rFC{KNkk!)wI(mYy+n_!E8hRk=(q8Lu)b(f-yjH zo((9_*UbDN8)QsA400kAG)y=~$h!2#@rG;nmY zyX1iYK9|$Lk<^~4a}$RdFGsu(z*hWNUHu6G5NPgkO!@Fx!TZl51CeaoHg`4&&qs7g z@^{_jEyx@SXDi?=Be8E3KmJKPK;Wo10&UN2>p-TZ$l|yU%U~oW6Z1b`qW|hCvryOY zrJG<*7Szu%Ww?qcy)ZJIQZ6l;gl3LtD8)qGW~d7Sh@%73l^Olq13c+m@E1-Q1-}c7 ztZzS=>j7mF1hXJ-c>B`(x)A|`2h=k>>4E~WzqEz`y65`l@KEJmo&wm~%0cHSr*SQK z2B}B~#R?r`I^&aBV1I28>~=(O9G(tJa(~VOuY{16gR>Z}jm?CQEBW-Ecz@ml-|8y# z?(V<`^iBXnw@6djlaR-74$t>-pl@59ep`3~M0(gb&4 z`sP@O`;GKX^h#*2yRqJ;9P&y&gQ+TVs?gOnbgWI{7YNT~dSw=s!z?p-4WqE_2+3o2 z2O-lZ5kS_HTbZJ}9SC8*f;=?Y0n___PY1oq_EE&OhHQT{rE(Y+5;ex%^6U98q_Hd# z@(a1km*XRT;<8M49nZ+)4Yc9a8CWpS1@Oe{0e{HlfT!CT3{>>DUh@R9^2Gy0Mz}Y; zy|l`$AmpJXmnS8y)N zt>oZ?DNJc47~+@@7JY5!OyydQ#7h_gy8`Q)DlM3X6f&_}0NtOd)(c5q(;#GeJL`g< za}{}Vmlb=^cF$(t`Oe-EH<1q5V8uD;!~|N2Y;j$o33{J!6X96wE`~0)US*Ds`8hH< zPc{v#MRyx9Pso#>$RwfCPrZ?ji~g|YJQPVa?!-B+%I()!jtGJ^u99l3Z&{kn?K2k; z#fL)K$^LbPjz)WREJP1xv5q~s(#z#angF4#xoS2WiR6Rjy&*g%k#;>jH;~2jQZ~3U zoYsY!N@Yq^vmT7~O_p+mA`)rQ;*a*0vmwvY?)zG4I0X(S|U{~B}wuHQu_AV&k69`Ft!E0afs<(gi?GGNj zC+WW+hJPVp4LSzi!V12lLSUvJwcelgaI#~))}xVZz~%h4LJ)g7@+oX{;7$;lW?e4@ zr>3T2)BTmYy%x(b@n%mcRH!1`YA*}AaGsr%Wst)sWuew!qycEoaLZRU8oFFh| z9bqxEnCcrHoth}*40b@3D`M>aB!pseer%PZD2@_#f=rEYSJJUSJ2 zU1hp^Sg{Q{(4+hNlxV?5N$F5xfXjP5lLHBiw%a@5hIacW@i>+q+#mNb_C2BL##W{< zcxNr=?$H!U;wg^zb1&hYJ9pNu*DdkC1*xsi%w`G?E4#JBIXSeM+1<*!-<_xp8AcN( zt(nmTcJ5VrXTx#_njC1(jH4BmL{Uw`*e+ypu7VwgiCQzK2!+I|?G;LeHB!2TIw-%s zg?qpy$M@~@PlAs2E%dUU+`nE0{g3b*hh=MG+wa3W>$^n@r`t3e)96lccXDFOIv`O% z((8Ncccev{y^V+Cnzg-m4?e_RU`qpTOmS z+zb5P_q^vlb0Zi00tA$QV25v^u#ESJ-%%Sz0swMitWe41xIH8f;#dtg&BN%sl^ z$zUn+gK8b2VMI(QWQ7BM#gBzU4g1C@W4uw5Wl9&E3>*f5EXP(1Tj8M@`#~w3#WPhQ z$kVzl7mBRqHlooX{!af!0BOfdXk?tCna8AxZaB;2w{sX-xJe<|9ND`Omb0ad{6wLp z>1dIUr;D90gg%*JA_N)YPHXS__V=H>VL&4BU->-$$OVr72@6YzfRLeZ*g76Ygndy! zQI75f<4ZgfHk4W%bV74xXr|}#Wr~a92~$I*|H@Ikgbi^E<9$~^qk|r&y05Mu^P?;| z-G=OoJ{dZCB>_>qpqwki^VANG9~~hK^5iH^l6wDX5UYxSLqI!^;OU5fkrv_6oIdgP z3D^lVLv_jnm#O7I9UY}Z>7zACfuatGS#k=Cq@v^UCAaM$u^sLY{yK8;uQl(Vh~Pi^ zgcQJkyEE-@dsvsNzxY!}3@Xrc0YCH8AO89a2S!iu zWMG6R4-F$o{FglYCD==Qp~*yTfeB|>auOXg?Rd{RZt7$lCQ!j+UwxYd$Mb9|a9D`L zuz$=9beto11iY`?o{)m0|4Z^-%b^ad*<8Y>6eWe>bN;6&k?L0e1*66}G`l0n`s2`2 zRs4@X(h*{Vqvh&}AGwB9bo^ZPiTWp?YDM>Q1hC~__d-{Um;d_P1N+M#fA{;( zonT`4txsSOta5@p`<2g=(LW`BHntMx2%z&u^W{BSq_X%NRUJ85_2VXQiUMfofKZf9 z6<;yHJ}!XAh1c$Oa;C$k{})sOlpYsAl_o=7m?chhhoYyXmbTDNllts*rW-1ZRi-pV zZojD{j)n_4pMiD@QI(m(0NT4I_k)B!E2Sp9K*$j?6igv|si0es~vRZb)b1^+m`-sxKogI+Z6z(j=%(oYZMf`_QUS7rQ= zbDTsMqB{o(94LUa<7spLg&0u)o#y;AH;#2353eMR@o~>k^(XBZN&k39a~=ik#OU|a zro{x(Sl1kVkoZ#|IO2=oF73M%+_m6PU|WrI0#(vf>=U(c}k!(&ZRx&lQx_-p{5gu z!2kOK_^Ge_;3wbt;x|9`oMZN}FaG3^0>1dm-}~$f580QXg1`OUufOm}(J*I%pD;qS z2p~)Z@G{j8#&DB{m&$ghoCMo^wP_B38GqPc9G*Sja9l@JOi@xr5*d5S=_s-N3@JF? z0~9|0aA3MIV zd6@j>7A4HAU!R+;n;MFFx)COx+c#4z?)yxxpI4w-UYkl}Yp^}AtZC-Z{#LGt5n!$P zgGe$ihAW4IY;F-x#^bX^i)WtC0w3}IzVd@#K>a9x#|7|X-}%+290h#a*Oc-(5(o|a z+aDo;kFre!TjHLqd5{-;{PjQj@H^h|j*;gBi4UqT0mM|kXm_nv^r>}gDoi!Ng|FB5 zyA^3ywvM4&A`5SEp3rFbO@ID-Kyy zJD$nS6{vh>YhfX65*edBLdyt)tQ1D?v`p({E6FtVNUVB&YinO_lKuZw`sW6fd2lg! zZ*g!3dZip2_e}5FSY|kr8Gg^$ojsFUE{e66|A}vfpIWy}tUwpaR zXjBV{wC+q!N5{to8s(M3czVQC0*K|r-E)PNa(SRImp;^_?%`|I^8EexEco09Uqx$S zcwoLfF_5V-3#uKQ1KmB)Xe$fc&Pai6Yjq|I$jui8$WCGNVzE;XCn3`fT<2Aw4u@qcO0Yh|*`{aik26FIUTr>hfG#vmF>z?;ly+TVEfJ1Gqo|lwmtPI#((; zaLt_0Vh5xwn?|vBu-xb`WzbADAHXKn)^7Ee8v~glJs5RK%ZgV9%JT!)_G*+SW{*HU z=|llHR9zk}vj2)yMt3sr$z;Yd{JS=z8z+{o)y!aHv2iQ2U>KT8C3JH0`&UcT`G_Yn zx{t-=HOd<+z6;#@LSmeCt2X>uCAr| z3>1(O235O7e_8YtJygh3#Q{{lN4vt62 z>kYI6T8L)y#?J-Nj(&ggoqr{N5(NL^&_u}VY!~pj1peuFk1!Vat|!d}9END`zI0tEM7&vpuQ)9iNFlK!3+K2d1gF!#kR)j;D2b z6I)17j^(j(KaU&^J@#i@Pp-W=6$(Z0x))YW(@?8Xl9?buRe-mRagzj110-Q=eoAGU zpa9CtosJI%vo4P-f1`gms!I?{pXOF<^d_L_+3H->uy#_JJXoPzA}vKy+XKldbw?BbFI_X5?YE{+9DMyWofaLU8w9^MwAIr5n+sE1f$Ve z)F|#o9W@$_#^r-1CYl&ECK}@sH7<#}(L{}3{Gf6Dugu^H%U^8ZM}B z-g@7gCw-bD0W2qL8)_Uu;jfvGb|rF#sC5$h*Gx!l6N-7WNNEU?3aC-xjNTj~eUuZ2 zT<<;|1||n(hpq`grYUv?nD+TFDcd-D-x$eNno*wv&o=CD^7~txGjHCs)LX$<2tfV_ z-6Ee$c!~zg!|jd^*OgXJ7==94&pH(wmfGLNAgLBZJ8giN z)J7EEk2Ee_PvGI>lO0NwZ4Zl4!vMtS!ADbjLz~-C6BYk}KH1|e8j-TqSaCfJZXU`> zGvXD&=H0r5gEQE$qve=e37~f`;BOVgs+sB=wJZ*wa)czaY`)Po6z!UnJq>qvZ@A&{ zxFUoRL+_ibKTq&Si^+db3uN%sXgIZP z2op~?(HBKIt>9nn+b4*;82m~w9#P5* za9~qIM}DIxCj#hgpSBwWC^ixf&&0%3$?YAU4*LNJ0?q<%DVuUeR$_Unxz(@EDb-cM zrvWJdso7@5qIm^0@z+O)eGIu1`$H((^^+X~Mm1=-6u{Vgs>3hW1LoI9-F5P&WCZH| z==AMQuZ|KPnNHCxDrGZ1i_%P-QI0Yx<9EwDydEu-q}WoDTvq36hP=7fY3{QCutEW>3(an?hZ{!# zr6Q~M#j+9JalHgRH{NXYJf;;f1dTNSuv~<9NE04y1{MX2k2Xqthch0uHY#-4`5VIJNwG!Jz1K61jU#|IkdV-)B1kQJus-DBfo__16nqpMNYm53Q5RuO z?5hBb>fF(a-6E+Tm)Quyau3FoIv`DnqKQKToAsF1q200b381D8fV}FGPzNmg{p{7c z2NS^VzIBjW1nJdi@4n5_L;&du;0Ry_0w@62u?ZVEfsw~IjUWiTl~%E>VWbQ9-jT|u z-ce}Z3Tc8LrKu5ZURqlrB4D5DjEf`7b~D~{LNg0TyONxlAo1c0=dX@t~zJjBlEk{HfS z_6)!M&@?~%_Tim`^WjKu9xBP_7BHDz2>~D682yP4ivT{A!{5`2cXlTD^WVFV0{;Au zzhf@sVVVVW1n^l9z(e4$uo&7m3IIAmdlIg-6)cP6+O0*PD-js6SB8OvV`l+?-fks$ z3cZE4IzjBH8Q?adPYS@uWzQf4MDupG*`(6bZpRhfuW-s-j|@fuI3T;_F%2HT^VQHa zu{|W%Cq!aHMekKHm5a>?(Y=Aq54Fk}I#@CS=@&>$I3I{JCW590@_wPAHVchav<|Tz zFf=?m;t;!+cn4IH(s_;jAEyqxqO2^~@gV|qHBbTpRN77Z&eMHxO0iq(CPN*6<49v; zAdcvXw;u!)wk~+7y*|m6Go`tJ2_`dI3l7DX8k5)UxAU#Mr)W(c453l1r~CFq z0L8nh)!vBvbpFg;CLfVzzEMXQYD#sbKKs5QKaEe!=tf-hJm&`bFYJd&3!6)f7m%sa zOFbJHkq9JuG40c225|N8CaGr(7D;aOK8OHbJ<`92L;#_GYWRD0V;0m z#*ttPF_ZOi1q$Z6GWbGlr2o#Sj!l|-ZYJZ$*)*aLHMyn-Nx9N;1p@6mIuwm{7XWms zZ%-8Z*V^=$))s)H?!?hi={*9_I=clLD5-G*QH{uOif9`IP-vi}=0ak$w4I@S5*hZc z>D9jqA92-q2igE07h*_fNb`**G>E@qNwJ_ zfYnc901)unB@F@#ql4S2moR70w(+(B(L^|{WZ;JaaM*|+#+Zl|+1)XKCQMLZ_a3`U zWzpfl5%mLn1+l`!gx%Neb_2;byJ(73Qq>UEV`NK|Sb&VcRtjJ<1d!^hx!b4nczNQN z!aM@z;RuvAbVmR&;Q&vpjd;Bhz!YSvwW+xQ7jQEGz@!v5&)y<52rgH5ZpGa)`{@P| z+QSR?)0VSspDal}Jv+C6f9_~lTAIEU5T8wrq*uWg$0z>7 zqZ>mADU|>&az|*`ECo>Ue__eA?4;q8`SmlYTdLdS!(0xGNCAF?4hTm}na?Vs!GV02&4buy=^)Ir6AI(R=PkoJRl!8|pzo>;ZTW z7LLo-)w%~-37G6!58ot~5NfkdWj~4lc~_W8*!KEl2pkXiar;Nex&{&W*6ns*{rT4cUVX>}@Tr{td)z-FfG0x0cOeaO z#whsj)Wd!AGL3<20_Y&|wX368-5@h5G$Bj1moy@ww(bm}S1SSRn4hzBHQAyRE>1%)0+d=Wq| zyn~>CWIAK0O-wXe=o)aUh;o=i7zvkGeKOvP$Pid0(2UNVLjc)ATN`;G|C?P*Z1a8a zfJ>p>J-0J5jKc|MdPkLqQ+<3o<&q&V1rYpYZG>BaS#L_IHx z&G3$m18dD+1Ws{xUYz8P8l2`#1aOoVnqP&)F6-bT;v=v)se&*H8;q!cQa4xJE@KVK-yPYj@~Y~ed;PHzsn@S z(vJo{tI*V|=M%t(E&_ZKm;c>I|ISDRqip!qUxL=%PXT}P>$8eNh(`VXPcF|GxHbfS z$^1PZ*TDd`i>{_3z^Az8W4aVkzy;QWSc{Gs9roSTpuuP@R<>!^KoRrU7@TPALoxW( zFodEJPzqRinS`3#$U`fek@5ijZ!IXrCRQPZWN}k-leo{dhp2z>JHPS1Ar~T(R)3xr z8WbH^!?u}CO<+e;0DIHiN(;BBpV)p8z+#$ZsfI-Z@Mfetp@NWvgeE>|>7q>oVV)+S z>U5tofFfM9I}knb(>HONLSI1);Rql*^_=e<0?3900+hDHbrS-Jpi);gFXwJFy~Q*t z&Z5Jnxuevn`r@~3vze`u(;tdQ{}1cMnc< z$dw^sw5^!V8t&k3-rw2)sB^eY^7S~Bwpz~cX5l@T-|c)U*E?~2-8}A~@@j5l7Kv%x z(j0Cp(nzfy|GF|@r!i4soRhE;0Mh?iT-|*OGkL(Qp?@9u-c`55+!=PJG%+W#0;Z|L zZq?z73-7ywuvY;okkPhQF?{gTc?5y}ixb+>kDz<83}z1kSS^&ppkBON>E0KZE3rQ1 ze_Q~u{)PG9e=G##T0lntpZZCTf$x_K{=+xVghLaFaCw#j$~r){m%tUXo=AMy!eMnS zhnDCs$O8(SM7QF-LtMs!Wp{bC5{1Lvk4~UtG*Q9uZg#sxD`8j;TR4t_8^iNyw}q!L zN=y=v1akSkBw1?VTIZW+Zl}X5jVY zaR0X6x0^~$rBbPh>D&D!pQ^=4eiWCB`wtIf(4TANf1%2ax2?UjR%UB?M+XDUP~RN7 z(;tJ{^InIH*nV5Mv5C`#U7J{+tW2B$gzi902n~7hX2{@HEI7Q^mvYhPU}|Hnn;&Ls zIT`QW?PzUi_1lY80}cZ~@5LxnBM8B6L=3#sWW~~IIW^>OJ|j#h?VZ?ickY)@~; z9GXU?<-xm06dBJrOM^?SpI-~I|(A_0e5@0%-Q z(JjW8=0qn(V1}xuxUq;A@kpyDy2xSx_0$YpE80j6Vs5+G&ZvaV0y^3b-(4*iwd_IJ`7S4B75DPZo>iRbY$vMGHdY2_ou5x-G79}tu@`X)x}T3#dnh+5P6PtIgK6u;jSk`DPyBx? zJGi|V2rO>*WGU0Na_`_kFnBT(?;Yzb&(HTQr*qmP{^teossH!@*hzyN%L708K?cDL z052I62BmP%`|#o6hu?7z`bQ!lpMcP4XD>=BXUcmO=h8K`C3#&O2`_TY(WsK*tg`xoEh#;!Tk^Rk~a=~~%(J&zM0)7}|J1+B`? z!Qw_BFh4lUYG9`WP2N~p3`|VSgA*;_bP@xTIz z=0!F2faE{qYlsZ1Ar0BEL73W+$=P&zA##G2jEg?qxAC)7VzaZela=5Jt$FEId@K#f z!EwX3WFkE_79;%r2!Ef0g2sSns0>tpQQCL_rI4PBZ)J^O)h7iCf@pJgXKRx3chq#$ z^G?2Wz^HD63)Q0$n^!jD@y$t!;)!!V%{harX1C%vj-{W59G-5P&xpJ<5wS3#`F{bd z27u=r1G7EzM^L)^4}zVdxVQXG0VEdi@=OAP2J@j;#8y|6_`wws3x5zYOm1z(W7&|V zABPMq%x=wY4j2ybL=ox+X7OlF4xD)6f|YbSJv$jX@w|rk5XZ8FHM5BVqMxT@@mU4fF0ky15dU^6y5mX5d?ZArkzo-=?Q=VItB&;$`cdIAk> zZtcu2M09fO9LSRq1Rx<6OL&8|3li`!Yx52M=UhWz;e#5%mu_vvs7LD3AB+csB!lIU zX_a0;FpjkJQO6LeI%SBQ7{z-=JLSbugV_*@FSm}LtnL>S{JtAh7~|Nj*6MY(;}SzU zzs@sy{)r1BPnEBoliM_W>&c53ZiYISBLU=AO*R_XdViWBNr?DLh_$Z&qifP}k^dJ! zWru}^#=iCC%{SkX*R<51Xz z1Xy%}!H3-iEueI%26|zCIT-+{E<_gG2`I6A#ouR+3e8Ga?RaN1*tmt{Efpwnx4*>_ zhWaTlr<1N2Y8I(VmWKZMsz*7$0BY(+sqpVX1&|W0i1;|Z2s*|A@<%(sEry;kZY*hhN|Bkx+ zkD3YaX8e5wEiQ|u$B+KM0G=}dlsqt&0-jwD>?j~+0q;8ozGTH9RRN^>IZQw_=`-5j z)_{|UbKU9e(3RsDPeEd{5u66p8Ap!rzDfz=^aa>N+bE6Yl zaJSR_Ss{~@pztuqBNOP21dl2pp+53V_~)DgSlbE5p~UbC9=uU2oNqYpCqNQdttsi# zk1+`EM`(sh)mYu!vBvR|=r@%FwJ+5F)-NbpryX68 z+WHRz3u<4+6+mY&xcNN7$?l)_1bO0R7eLMco=X6~aSV?Cnr}TB#US^e1w0cB1C#U@ zJn5;AycEqr;y)enUrhihYDA-+e!NciMx|b|jlK1h515WFbK?1>w49O!K?tgCx-{C40^A95S=;irT*zu4IL={r(3%v$;{rluaL$BBua3f{$DdmOsk2IH*q!8}|H&q| z+BtcCA%p7b^(*Z&902|CU+F#|C|^&n>!1Otfhq)T$sD1c9U?sK7mPhb*QbDGQqomag!QM;tSNS2ES54 z;cN0I+RL3(2Nc%~^~vKM->i85oFP)K> ze-0+hS+G87`xrtS=m8n&Hl+N>~M97Usqf#{IjH)-crh0}tc{!?LxH{~AMZpz4;-Qal z&qht$4F~*%193&biNW^~I{bfJ0H2Bx@X0lR=c@;P>X#vaXRHOp82BtA%nvUO6i^}Y zKlni^Afmvl&O?BCu$-m1#?q>tMGX+SQRx>gXoD7)i%HL~<{?75n}p#bPczkHLzZxd zowvN>N+gyK!JzaN=``XefzGC;=BfifIr_=4OqX1RjMxPf?2NMwG|Q0wu9Qdq%4taN zzyXz;6siM z7|+Vfow~uLWFhpm;dW$v)!2{!1igMW-rf_BB}ySRXDvvqW#SVRZpPns;`8r{3r z9v@Ar%(4g|Hg4|p^c2uBs834V;0h5dv=_z}yhe4tO0z26$z&3J1^?iZ6`m0*lwUfA z(;R!L<<=F9fW1r1PMFq#GFuctk;2)Xb{w1z;?l&ar!u+OmU90{Pc zI$R8-OfK5<9=WJnn}fZ9RNI}!&e)Lv>XG(9$_Dy2v#}O&;w#jnjdc~V2s^M1_WqGi zi8tLfk?NQVbj2%DhAa{iDcn6wEgjBx#gmNfM3W3z?v!d9@29A83nBm6!E&H&3Y`3y z;d2Dg`{LfB(`F7vxy`^@KalU?cJ4K}wlN5=CSfsnW7p@G%v7q}GZ{Q#srSjL=5kNxUgIpEV+)K7l($$w`l;KN~$DeC~=@T?Di80!F!1hACG!-X}f+j|L1(_ucw zEwpv57+5lw z=C7wMUA@`a0pA9zh)i3t6B}VKrk?oCw=D)x@KC(Apq96A;2qrR9B-RSy{E6In;Y4* z*miGm9;;sG=gTW=QVGIxUg*So0yy{nxuh;QDt+L_+KshqH}=rrsnIhV!8cM;vycQB zRm@zU4IYiaLb2;JAXiV1FQ);4IOiOo7FvD#p$m0*&4=%}m(>1g0c2AG$fa;SP-1cDY5+~eZd+W7Tvdw?a-#=#GB=yhG0fh$L0uC4 zdvjX{%T`e&8}}N9V4|(qn|IfTnmvs-qkX8E!`Re7i?8h39smtc9+DR9AUe6V*P8N-xlTH>hi6~vDOl>M-W zfDALbl6H##=90ZONkD8aAhNK{-QB;Q2S(Bln}d9GePfnOv8an>C?&aFtuQp{mV$G< zDF82g#rE!9J3| zTBp^+S_W|eg$;~?&ZKAqeQf|Hhc0>5p6tJgi0( z=t9^gmXjrkUvlv_koOu?g@2ih5g-G+g+M+`ryj`nd*-|fq%ztIkb}qNCqH$4CY!@i zfCIfgu(gd7;IcA+h0P$(xD^ef-)O%I?)W>hGmJAgE{?mDeJ8x(-FnS#NoOvyj^z{^|gc2`sM0OE|pC_tzcz^wEq+z7%zzaO_z$o=>$l&iMR z3RO)n4Y!eR3+@G!X==Dcggof?Qzm{=G|-x(K+|K_P_h#W#^=H1F*AXbhi?n^-!Kn_H3*g5qP0i-woSU<{oWMvX!NF-O zyuus#Ul&05Ae}P@qL_duz2VNxf;<$)AQV8*uviW}zxo6bz~t-OhD;&O}qYdfa zJV2Rj=t6MYFfif;JDQci1X@urR%Oym9|Jk?DFD<#Ed8l>Ft(yDP1t_GKPZ9zso~@! z=sf9NyKRGr3M$`~27jvED-%NiXLWFB1HYVRGoS((UxIiPHZq#V$7G{b+SxTB2vq^R zDu9!Pl|0Y98M5k6b!82PpI6<0RI{tP5b4}Og|ESye~|0^h^Jhn*-Z#&UGo9%K$-{+ z{}h+24LF1I3E=9@k1P3edcXcp27hbmbliYsOan8l9z6TDrv$X^q* zsUx~A?Cv_eL4ZLM?jt_l36BKwuLGVdfZvN&l2N8XK1+ubK&O4Y^`0B+G!ecB!rYCg z?$0d7bb(wA28aRT+Y=+>KEa&UQ2@h!9NG~;F)t;x%SGoBa1*+^eJalk;~3)A0AB_6 zCpzp^iwCT=av7Lh^KiazaNz#}cuoQQ2#eurE1~}h0esbE6F~5=UhdUFJ%Dwo|A@xi^vSJ@syHv{y^1^#iW(<{TE*bIS4?3UoX zPg9s20c=nL=*jF;0I}y7LQ;Gjt)a>6jj1v)Ut)hf44PomQfhawq|sJb^L9o-;!=D) zGu1yz?_eJ}?YtU6*G=_T&J_U~?t9;@_5SyM@f|o)-9IgWI9Xn+JHBy~=uo*@TSlCc z+3o4N`{5;z>%({Z-iAp$rsdrT;c)NxqQYGT{?#o!#wdR$iPg>ZnOQ_jgh%C}Ka@3s z<;z*02w*=WO{h(SFlH)Jjz<_|BMy(Z1vrTZ1L9J$1p%a@!W0HUGzflt0lnjkpOSm< z-nYK@3hc+C-Pw zjT-X-nz=$*mX!cXS2gE3+zq+CU9d|NnlT>O{LCnVy`V9n%+m(1BLi;#AVQl&%rOG=N0-Ich&Dm!>CO;%KPeHQ-g7(54F~{*QOzYX942mU z@C%y4(Kmx9Cq7G+*AxA&fOMw4IBOx;@O77McIXD8L!0y4qvsL8M+0PcOaMQ1Q3A-- zmpNZ4zF|Wz8mZa-JhKy0#^>G7hy4&2h`>QaR$hb19d$_)&U0X_bmCir$;} zO&tM@>$29s@U4!UEyBrfm)M`uKMDb)Zz#r6akbWFa)q9IpD23VHfGYkC)i$x}-C65J2`623M^e(>>r+a%|Hk-H-fe zI3*QwSfZ;05DF;$qGNv5Cu6vC4l?cNBmO)=st%p=Gl-L&Q7k_yjOlq`-3Vv#zoEoyDpax zq2Tj<<+X^c6}D*i$*@XZRc$M2AE)8Sd8Yor0HoE7?vEN`)i=1C=d@@OrVPXDokzKbCVbdfCx>bDU>0*pYh9E2`zJHEhu^1&0f2iFG&h*I1raiZ0=s_i;e<*`~xlX zX-%U~e!+QmL}*t=g)olOw3`_ex;zs8OT2cq1WN+ZKWsk|Kro`)n8A+rCsbmdqS}u+ zP4(dpoTlL{HW&ydSLgG$3&~2*_`d+2e-7{$=a~b%Pc85z6hO=YG6FojCzc@f+3m3D z%-LKCx9tg8O^=P+9P!8_YmcW{0-$Mgi$m;JAhI`l@^JQIk9Xs7cX1w#;xYwUqtk>J z^idhZA{pq9b_!@JR2gh*1kMy)8gp^eonCfyjLR}SbSq}KIbA;h{w4j(*8R?`7U0E- zbxB7IlQth{jrqQ0y{z__Et--j;Gtj_#%d0h_m;Y8Ea8F21-5WpMYq`uXZ4{7tuU+2 zLKh7y(v^})wx>Y9{gD21E!e~O8v;lLz;qRUy`X_y z&CM_3CU5}!nABiWmwTNkKa!b^G$wAxmn1;urUBMuzTdf4l9z@}Q ziAJ-B>j_p$p${Ca+a_r9o-2(uQp2*RHM@v3hG^nTQj245rhyq%4vmbEoDU0!nRrOx zFJHkYlZlTprN7pOrrAVdHIn{x_ zhC!?=Orbqmp%OzDW_@P$2%3^hZ$~p`*zbVnd2oc9bBh_S*T6+Kgy-*_$5@t&#S_SZ zO8AE?@Tb1JiXd48kSkXH=_t^O9Yp;u`fr4zy)n0tnkDI8pBRshua=nEcH$*)sr@t- zZnCGl78K7>5A7}HnT3Z<%S30wEmhBYVmM$cr(sX3a{xnS>}ZuRJ7@ceYM$sy$m@c} zz+KUsG6&&`-r5Oqx{e7aSO_n1rUJ_dB>)WK;OP&4_v11E9$D&NkTehzPV;6XU9#}1 z=_k)tmSF;(52IlW3OWG!&hg|SfVXV0zy)IO8ow(1@Ng4whr*As)qOPt^@r2nbt^`X&DTO&@X%0JD>H2`2t74 z{FB5Axo5)|5{t%)V~)9?zR*P|Iy}QYnR2>>t&gUzt%Rw7%$e?|Rq>kPPTzt8G6aKC zzLb59bBNd8w;tx87zx8cFFS@z4h4qTG3e>I7Lk5sU>(K;hG1MxY;LqiG=sHi*5qKy zB@4v-P|75}QUq`}4EnveI!0GGfMc;;Y$a-ns~b{_v0P5HiLoKpd_?aQaNufHpSu7} zh5$A<=chZF?39YFHJEZf1sB=sPrdyW=M+F+>BESAoFyP1H(vn(ly(sUsKbQ0|2<2t z=CB>W`f@ip+lm--HL~))H&5zE=l~rGNUMP`7OZq`Iv768*A`aVxaQd$ADnb+YJH`z zyS8S?N zkZ(pMNRfYv+iPACnjm!|fB*=Y>VA;cjUzsaFY)I0z1~}c&7fv$Z@Ud=X-98$W^?os z5+?qQ-nO9;znz-@C`(TCb7X6Sx_kM%Y6c7f@WmokipTk91yHf?;YpA!40rYQb;c7U z5LDS%nudZt^!P_tkt#7oY$=RwQ<9e`UY_MwVLTtj#fAy3<)MmiKRD4Xp;0PQLxS zAOGUW-2zWjofL}ZM)x}V%6-dY12}Aza5ym9-rv`Eu(laNaWA*X#(_!&6>s=kl{JR7 zIsAHY>m=`CU~U-~)Ze~&e7aAD0U$a6aq&MN0iM4W__N1rfk{L?n~MLIhrt>crEtG| z$K7|l1CAhqg4Q&sV9A>vL|^M_&xV}G8vWRq+!@?2m#-JH!5S#b%I;l9ALxvC8&$#) zN5MOTD=YoO>49S%Ke*Y`U%uNlJS(A|Ji{m*hz<7@%Ux?}=$D~*2o%sk^wh4N1+PRP zj)~aV@?D%!f!;|%3xy;{*pit3ig*pFyI{5eXVh6PI8;Q7mHMcrpWl{Kr_*VrqV#naXBxIyUbmYAaLR=s#mR@Ual$dmPCqmNyMJf3o zAr%PEr&^)SA~W@s{5MLJ*n$z<{76KI)NG0nGq>BjTFMFcLN)x)U?hoMEzLP45j&hw zWME(bqFdwM)Tzb-lH+`@+}4dsEvlTsYE>70 z^a%*y13JUK>QccFUipkSl}aUnVPP5cRrLs1!Af_eg2MA-;1vU10POpmAV#VqE-h54 zWCx6#hOM=|Ep&_;cEn+9hS*mG93#9VZiCukn-_gjlisYXCag%M5=lBWzw8ZC3>#!$ zsnLFcVAV9C*XX)HkW+K(o|AjibmQxEU0nat%@tlnV-yFNNk%^|W9GF$ffu zwD)rf6@rq<3K0LX*{#j&zyQCkdino!Q*?Yichx`ueCoI)f1SX7RX_4KSfQF80!pRE z2h^GP)#N5?C;6!-VWto%e%L@eV$X{32;uC+79(eDzSMmJCK<#bB_DO_)Sd4Np1}X% z&);ClhWO*1LQ^U+mWcGPevJR~0GS1Oa2(bPfx|ir35`lPwEGF*<>CSU_-Rjn`qRY_ zL_2B(IzwFaqk)#w6R1=*yW1N1nH)K`+5@c+C6pWqA_Uv4z7PAJ0NQDeACeM9qsp1q zkDEfsBd;CX%0u!W{ByeDbWanBHH9w}_TOnpsFDvkf&^QLgC9h;#C$}82&$EAk?wpU zt&YtR)1f1- z+8L?Y^UK!#zwG}!!1G&zsAYghAAa3e?k|9!_ync^seW&{KN0%U+%pkC0P$;vAnd+Z zAy6P^)~V534I%81=^X)7vLV|}_(BliCLlLw4;e0TYpq{N-_b>Us_Ib-xE_7_>BbrH zgDQro9lF;3R_8e33nU8i=;u=a|8=bflBXcZ)n3%`)Tq!wRvlBTe^e1^g`z&7quqbs znv|VJGShSKxFx~yJFjWS12Oq8Y&jvN_q!R`_FvH+Mge3k$az@^PpuV#oIL`5)%~5( znFPKx_e=?3y)aOZfcSL?V$c+0^q61K{Q?^8uNa=hgHix%!T>#_x!EZJR87=VuNiy) zjsB}S+03J4=Sdkni=|u((PYEFuxZ-yPJN*Q$o5x~WhfBq$zOo`akQ$}op8peQ%)%y z!;+|6h3ua`j0ks~im;DE2_2#$KYiS&Nz)aDw5EKDx6PPdUI7jeT+Juk#{x;wATg8CfLHbxNCcM~o(qSZsF!x_2fHDuO)fBKAbXVEhH4)Qt z-biJ@!fic{zPG%I3g%luUJTyvv5peKqnVFz=W%kz^dec*PB4TqZYJDHa0ZC zbqrcn0D}VwFIohxlAT273OTZ983O|^jRdhIzev^PHqn1DdC4t*dqS}g>3&9z`x(P6!4sda8B;x%1XQTGgx2+V5eSPKLUhs;FZ{86mF?+y5dsn%yb8R-nuEvGX zOxMn~zkYxJdSQV|DndfgTkP8PyM4k@?r(hw{TFtdw4jHc36CIr5>TC z3_(L+rNTucR25u~Rjsjdignma^J}9P-WO7+oSf@RrIrB7FSaLg5=(}3Z%^M{)!uQp zvs+WiEiJKJO!>iyO%-pj5?hqP2g>vk=rZO+A68*&ngKKQ#D_?}TXG8p~)F-F!PO!QJ=xCOvR0GIJp>MvpGB_&F6AmqUsq3=`{bTOGw+Qq9 z0{D;30zv^F#1Z5*m;yW_06fDG1i4}4KrW3fuvCJ)>{(n0y;^!MLO_iv-|pdniDswn z-UB96qa!!UY+oG6NURow}?W-s*>eL_VJhbP*mb1LmAJ-8XIH;}i4435G}L z8rIsh9k$V{!loJCy)`NdYQVf=9^J~Enl2V3urLH(e2paOGy4ZVBHB)HnU$__)9=7_L}M28YOg0NVm$hnqN^Rta`JL7+6y zSAvef3rx)XUJA;7tJDIU#khe2VbTvrJ}&G-{3ZE8r;)19x7yd?LTO?vYPJtHxq@C? zmdZ;mLF}{yIwvhh|9lI56CEIHOpUj@eMIj&!*}K){F3>5Pk1a=0)mLzJ$TsC@)XwIq7=^Z3BlKzJ-CfR z^~NSd7G0}&Vxeogl}3HlQggEh+=B8de1JGMHPXa{5HirRwA}3zdo>Mx&F@e?hHew4 zMX5^;A1=92a7@Z670@t(=zyKkA-~Xv&H1^Ua_#n^SDLLZlqb8UR>lln6_cjB+B#ZV zP~X~Q-i=u~(Fu3D4_|qurKQ#1*0lfytm=@y`R4caQ)Mqil5$BQ&ygojWLP^qkPKMD zagJ`_VE0zqtppI)b3sjyP1`^;^A?T_y&3h{Dsi9@AzR$NmGvp9hcBHz*&p>22jO=u zrN-Bi8g&^z6j>Gmzp(>*UJ4+}E6RST5;sBsV>+8wDol73IS?EBMlGF0Zg0dSV)6x-GOG_j=+M>fHI8-0gFX^K;sEcW7=%d>s}OKK235ZZ1Uxn#cM+?^;GK+8u$B%0lqrB@e@sJySdR-r z!u6z-Vhz~Hotbb!Y^wR6_PvGfXhyrG}*Ucr~4_wjRSGcco7LseH>yvNZ9bX_UM@HQj)jhS?vDx3_K_LKJ+UI-xU6n4U)dPT*Fg`bL$sSv{ytZfe4>?}~0Gd`>nw8H?S!J7s?O z-o04jKM(=dJ!ArS4rdUiK%ROu0{+$~?yG;lKSH5C1gik60{AQn;6&jGk_S#~XV6(1 zq!$2(#lekUO83%hh)6!XvS-q60X>h6+u$=nz(7R`&%|<=!_jhZ<-nd0z?K$QzMRnN zFa*YCK-onA^BWkN3gQ5qb_R9ON`R7pNYJT@k^Xg)N5SDcz!^?5zu@lfGaJ~eh{eor z?1&+THM?z-T{QxnLMt(wlnM9PhDk{#cv=}%hyVsRXUB#E(PCjW{Zi`!dJB7X{5GNN zNQCyAA6_2Z z;jxJRyS6*jNXS$RZfpLI(9Epl;FKST2S9c_jX)pF%Ay!wr0l01h~H19RNAMrt(L=& zA0appEBa>px^6o zAR4zNgkxx72V@Y7N%Uz7Ak+qrdpNKcs(aM)>)=UYBQR3}XcwVDvbe(6y`6D+U{$u; zPW|I-H=vg2rEDPGM-RGn`frbJuPgZd70fuCVE^Fi`$?ZTdNd+X`+m zv=Hl!=~M^qoH>2_V3GoOaSIH`rosPL!jwV4!~+sEyS<)oMo0qS7frADp zF2s97cXnuA<@Q1VNhHNzKX6*JIujOpq|c;NTXP2>Gt+4wrsaGH;TQov9yTrDe@`t} zB+nfW2#t)0NYYD_*rcBlz_+_~4FcE`xY_WSEBG*iMIdr5jZP~xN#KZpG%mcABr|r^ zJF`xc1yEXB?b|ne5FZ&7yl;030ti#T-5dXj2=F1B1C(jNe?$R4av}xt+Alx(6MtL& zsDEcs5ZIUle&nTTDgqQ!5R7f=fy2^b0nq%yH$5aHd+b4Xy>946VL||u=mIU6N2h}k z;Zf6!x1e?m3<7&xcoy_7!>>!ix)w0`L;%@zpoRf!1`y7f76eD3-)<#-8BqY|_GBK* zS}0Oh5b2521Q@{NM2`W0E{<{vrxh{iN!JsgDl#yEGgwO87A<(1VMlaUigHI_%&np@ z%*)wGA=p#9yKlpO7tsuDp%*rJpp*6|apEj}2_n$Kacl@+e)rbq#T-Yk3Shw11f}W# zL^NMa)XTr3u-z&inttOXEu!&DPYB@1J+DPb z84!=k{ev1fG3OTKOnVzl5-_L2hr;fS=M=XOJ}r$*w!qkOBkqIvh}ZB=ywI1Lx_hs% zqp~GuxBn+WE=U2n1_lCnCJvwDU+N#**(|`zlLI~yKm>p<3AFp3NFdheBAB8uMdB0d zNv(P7nRXqUr&w`lO93oc;*VR*_*yr3d@9Vtze8v+9%TVIozp zXTq-hVwy8}Duw)F8v=%g*g#B?v^x3{*&VH|ri-m(3NB!7Y#p7+xP%Bz_nzrBG36|! z=S`KF#OGZT1+Ii~1Cr`$p{pAMPzs=1mRAJRAHDaEC4XSK(6eb>h%iynd!})k`QDh% zrzs$bksRGC3{MQ%cMFAhMXy7<5CNoE(ZtU07lQwg5GH+mAKD+q-G@*X4*AiR7W%T$Xh106*_A20(HJiJ5drE_>E<204FQDIJ>Jy< z)_jOjksCbiM*PN)2fWs0j>m&IB{c{5-l_n4R^2+i4`K))J#_!T%W~-*-ELY<<+g6w zv}mFLdZzjlI=!3tQ^8MxJGP{HF{WqnN(eMU{tF~QeD!DlPX8Cc^Z3I-0soOj&`u%T zGkUn2Eet?^H)ia~Oj@NK<)1>|%3cfgzOy0XsF?hjfXO0RslM(e>MFT=b>-A0h*G zQ#87uw_6MXeBhzBK8E%?9(WZ^_gMV6me`NVxEMrY69YJEok3?rN+;wFJZjD?Q2^6L znVD)lOYs1EK!m>uHJ-OYx5i-gLK+po+m$iFDfSAYL8vUoBYemrz@_pD z=+GA}fB+@quHx##WhH=8*M_vk3bP{BmC!o!K=5{l-3Erhn3hOtbd{r9dI!Sd7P@lG z%vw6j)%5QDybD_S?3g8(b;9HmixkMvi zwGI#h2uqObNeCdCHU|}f03w3(v~?wAAv;Ut@Stx#~D;^!D!TZdW*A#s{68=dOH{bc+#CrWXW3qr=%3JypEg%;*Xh zvPfog=T-)KjF5DRBVZ{&4S6{#rvO5dVbxv|nM)!y456egxV=#*xr2zz*pZl@+}-Z> zajbel&G!T(hTv$6lxU57hiGYgeKA(j)M}dx5J1Myg!NB@I}27k!oYPTL4u9|27^}O z;C*kd_>}(1ZC&kz=XeuhH9IhzWKE)6K7ihkL=R>F_q;e|5x|vvb0Zp!C>a6b8<@u^ z+9M32n^W6cDAB>^HSnY{v4~`UQ^!;v{o;9{tA5g7nFFQ?@)LBGgHAvJ#gVCFqNm%^ zI1j2>lbr!q1D3%&-kk4==wy3ww(_Ne(IGpW-|gMO(oRJ+Gf2eUIyl%VA-?^;0A5)8 z!mlt4K9d8Z0zm$K>NCMY{hQ(6pJn|Y#qSIOyhJS2qa+9x!MyAxZ@9Z!dZOsxRXTQI z#^*7xE63yRsMo-?08KgQHra_uc&?C+Zm4jPx;yBrPrT50L5*H3~4L38zxtvrUV_tu39ur-rKb)UlnL`L6$-#jeGvOu* zpb47`#g+?N-Gk$H2S({l9f!-^Zuwf&TrCI0L;a@xoaqf|xKZXLjr_QC{Exd3Ugu3{0KSNvn;&2Jmff;CjWcjv}VM6Ej% zwKKrfE~l2T2(a9aB)Xu-#8sd;VcMyggSgkHIgr`6_09(Z6NmGMZDX@OaWz?58`6foQa?*ck(s)X|Vid?kSNU}~j3!EJS9 zpjef7*F-AS27G;jwJ8QGQQdHgY|CUaIHPW5o+Kk+aNdD4wsb~>7f@p*s2ts0Ie1${ z*J^khk{MWu9MWoo*NbYw0W<w*<85nOFSU#lCkkvr&^Qg|VnX;o(Da8m0u(|%CGfrj;AcMdk5vLVzMW7 zl4c_b**ArTSP>$WHqff2g+g0epcD!e=mG*!iB$q5BtRhe3nBgh0YZRS1QNTD0I^Ce z66f3*$FbW60u5lu`}&14-`F#mnRn*Cci;UT)&hU<%d@l5W|jh*G#GM6(pzh{@el_E z{$WK;K6EQ=-1P5$YJ%g~$S1pi^V8@lvAw#zHT6I}=L0y>;LP@Ibk(%F>!HVTx^4E~ zsonGz?c&V#YWqmqNhGqGw@RDZT3ua@ZU(7-R#ScH==S#ZOdCr4(GHNINzDJ|!-*;f z(kza=@(&5bgCa=`me23oeFc;}tzXUG9HBjGhEd?z7bq`2wBk*wio4+}0P*ozhHf~D z-(O6-!cSows1E|f{yWpLmnPvpnGzxu;( zAb+nT0AbGz$lJ_C(B;B{F7N{44UZ=0d%@KS}n*H3h4bltz>Xw^$Y() zx*&ohWzP;R__+uBm2gT_lIqzC0SB7IdFl?R8pl;L=+i~A#{Ia@!B^7@)$8Ba_Q6)b zEqpjZS$#BHxj^{QI3j)6mzH4q1=jtKyi}edbVvc?|ChG^0|A5tVlMC-pZnZbpn}jr z{KKD;0lt3j^!jy3;PvZo`Xa`=Xy(r!Q~mz<`_F#%b!Zg7Jp~Xc;BS`?2x}J(>*@3Z zDd12o{<@&`iUjb{_+jk-uxIM;cIZb$O^QUKMf^T0pcTI41<(T63j?4y{`$8u=Kie$ zD7Qu36GyuTCRTZ*R9Tyg_OB?tQF8cQD!4DW;(wfXdMJR>jv^!couB{UC*Ob!?zC_G z{QBwj)312b```cmXTIqiLcvRK`Q-P$_?JKb`Okm-&2RqnTd3csME>4}A5VRqDBWI$ z=;c!eTr7ZWCJL}ZFuptJr$Wyt@!KyTm}FbYc2fKPQ2{Ld1|A)!fNw6*NGrLpO8^yf z&;N)bsdB7;AII(RG>hyY&879Q`{n$I@F& zb5{YjXhl!6N`$G(t!7tbgs6~#g+IQ+UfdB~M%>j3!JVv=FjP9w6@R6MJ$LD!!adQ7 zLrnP)``^FRM@qr}A_)gR(6vI0SZuYBR`6%A*wW5sSn9=A znAnrVye0&H|5PLzotc8%Rged5$ya)2Yi4+goNmFq%2waSnVIJCAP>N6EP~33Jw@Cw zh374EuP(qFBO@a?BW}yPEn{QzNOK!ts{DW0B;J=+!dHHp07X6({PurmQ~#d?5b8$> zTwg!8e)^g3Kl+b-2$A47DS>$ze5z>Sjs{wDgB$>tjTOjl0sP>#Yb)2Xl}Lhc=8XdA za%I~h(a3O+Hq%0pmlT9^d?vaz>@+eyA6Gn^`q#5Lv$ZwulZO_&Vs}{zF%unW%i1dn zT^2<9`R0+4m=m3oyio3ok6*;Ld8ZR8+k&ySy=`kd+8oQcl!A4p%ZH9Wvl*1-f|3D| z6i7M}of!{8F!M89fCpCEHE@!tb`pe&Z25YIXPW0@vQ@V1PlBKgyf9+7U07@~KErFo zGe5XByS%7AA72s#O>6k8;n#d3fXJ(6fbQ<|cszX&;{KDJ?HMVwzgi_GK?Jo zv^&$ZjhwTxaV@qpIcw zkh0k0XS64`^C`r11E;VwH@3Jq*WVnZ#xbN+?L5t~vC+Yy6{n(#Ln_{+c$+eZW%zxD z=G(a^6+X7Vb3qRU@Ski@Jcu9uTz}L1p@jSR;44fGe4QBxLIaEILGagds{z?r2J_+% zq20Sn8Xy`y;f(pA!7(h5?V4@0v%iS-N3a>JbdP5`746CRtdL9bZQZ`SFgn^d)a(?q z4ha;Uk=D_%rm?xDW&=-`W&-V|+FR%5#>TFGbcMxmrU0r~vdbOsfpl*m4z&Cu+6SjS zH*O3jF3MUAu8vi3MnZ${>Wx7b&AhKFD+clLR$$t(rvw-5z(ZYKU5RFA9<+|Lnu?z9 z^o+?F?Oy=J?Y8G)kB^p|1*h=gHJqlW50aoAaM8k({My!P|IpA97Tql^4GpcW66!@) z`o_d)@+^u&lA+F%W()`gN&!5kyMb%AN?12j1bh2*v$%UMDliIFNpG*NrPkJRL-@5){kOLynv`a2oxi>H58 zhrs(2KmIuf0Yt(Vvwr*k{M$Jewlt6mh#xx|ND%(Fzh8d(C#7&Nd;w4hH|{ht24pd? z5gDA&QTpi)j1Hw$%i1fL==8cBHSV*?={1X02_@RTe9Fs?(bU&V@Ep5^-Pt~8qSn2A zc-*NCkk53=<87&~uMLlNQJTvyeX&REHE%}-?N+rjyLT?!a|GH9n(o3H568gE= zozTB9${S%H(y?&HX?mG9wvXvj^Icc(9UU>GJ{TbX<4LaDWD-~I z-_D!2@7)fQAp4N7d_$HOL*IX{z45ERESe3Z^5q4v947$trEsJH*4^mMA{Rm?d1Nr@ zr~-Mj2Hcl5KL;`u&#?Eh#0*4_nwkM}-lWU>qYI%HRKkH>>KzpY#4w2b^cGt!t4YB= zavGcnQ~{LMZD0b|9;?^8R$ML_!KG6nl(Gqvy(!gZ)w0cZPb}KVvkMHIN`W**1*QIl z1@q$eZa~;105PJ|A-4n68PGRTh^(00!N#?an?P0I6^5KDz{wm`o$=r_iE6j$gEPK; zr&C!VmAHQ@;ZlefGzX8Hx2`k^icq&rrh+_mk<|lfWJL6XvM6=CfR}nkvML>s)b#n| zXR!@H0RXtV!S1snKpRmMRS==jkPv#ruEgb<^#D+d`vCzI`xM^NV>Q*)*jIlIZnQ-2 z?VhHVFaSU9vu=Q2Fx&;qwlNpSp2ti_gXxI8l~fJFc;wn#XaU5tCviSfERlq;MbLg2 zps;a+p?W5=OrEW@mdOi2!w>9>E3yFY7C;OrZyq?+@8s2!DMdi}vfeZp6fjd)yulsKKcmbCR;5#9JFRa%4B1fJ@0d(f#Kw%wg z-~+FB&Sx3+RJOGOog_>?Q0BmSRw)ah-+Li`Hqr|wy4w_~#ot^$9&l9E6Q~dRdYY`f z3M0DFVXGtf?^vz7X~=1Z2thtQ9aSWUK-oILST)#v!5E~`2Y^~oKmz?#+SGDXM-*3$ z+tz^&$oY3A)_pucKuR4V7=3WXMBKTCQ8e5DVQO$BJ&~-LQQ15Zf+G+PMQlQU&;BDo zzv>z~b$rOKF_kGYQy%0sjxc)P_qQVYR~G@y${)JH?HPxZe>)V^R%@l9n}DtK`g6l zL)}w~S+5-m*dR<4_qxecBLhZtv(xLSC3%e8viMNu8IssA(zCO7ZUma{-5udug`WoW z5dwIOmpUBQJtGD;8VQe$*fU0eHZK4dCY>TN=?>R4RO7yeF2`JQ`4puGUwL&=zj0?a zii)GX+o1sNOa9J1@f_2>kC4BQ{Ni_Cf8UQj@PQ{w`@jc&^rNr;?n|F}=&qIo%F;ke zAi}@$)jtX#k@&BC>Aso$&MztBw{!R29PrNFTk4;;^V2>%Ooqa+)1XiU$cHuc z?*L;K1wcSdOMntKfXheYO=Nb~;(XQA*N~K78;Tk-UFu97=i5T?_3W0Tg7|)^y{A&(cf#{FxVMKuPs&spLMF5?Nu{xk&K*+*Z42%+7#^P-HWXVfO_kgKw zj`a~2#uw}Aa60kVMEB0w9}3`p8_Il7KTQ7K`1-H^=*f?0Kl=JFKJvf^);SOs31k>3 zI#^~D)CUFd1(*hG$z_=bQ8pI_9C-e#WyBLo(E$!nW>V29fRq*wcvuiC`-rE*%>isZ zV@2?P6$SJ1{h^L3VpG|?c-%G26WBsXd5`-qiMD=y`2umgb+Gma;GZbnGyK700 zC%auK1eG>vRR~M=DS)bL+^_HDU??f_*sNYtG^!TrLRU8Ytaj6*7K)bneadwRJbfF0V4j zDgx-W7bAdEmGo&g6RF*3FN*n$&gomxAe>wk2;fPNH%tMnL(hP+2$Ij4Iz_1z za;h!@$hhJ>loF{|*8OheO8SUIePrU0m&_ylxVztLs~umOzyQV1|r9Mw+4u`oexA zK%CR%(Xqvr7OVofHDfNVvqJz0e09{|LY)l_{Tl?(wbGdkAmNJ}VpsfJ2t%(9J={`N zMO0#qdo&^(i`@cPGav#e=&|wi%AK)}zDVpM6T!UxjY|`fn&nlkNs85x2Jmf2KH5-o z3^N$Hm|F3aCuP!omM15!bw?+Ou9itjj#IYssqk*z-_~R7(#D{|x7!(4xSE-PVZc%EH7zM50@UG^Nu|XvG&pZ)`>Y}Bxi}tMnhd4d zrh0rb%|>jiUI$(*Mv9nyYw9%)S^%lTy&hb{fzcH)I^o2uE7pZAu$sbjh1;8piL0|( z7y=`ZS~4V=P$BuHyPknAXJw_JpYez972G4_RuHp!qL~V49giZzUHn?ewjj5Jgs)0l zBOA?DkZvC6j2M!c;#G8ABmdg2NecfdFH!92L0u0+{?Ocj#hL)_;pIRThQK89yW=Se zt<>*eu4Mqr3PK-cuAdkg#)a_tyfcx^IWtdq%Ao*0E&)8p@UL|A`^KL=c^?gN;75u- z{==Pw@Tb1})t8=o_R%MnWupL^`uFC!h$(=^)!uJ!TM7mEtJPp7Q9lyP_^5{DuGb*~s3$kh`2G2^ zVwx2k--x)lxLGC}u>@eW!;M!boQ^`%NMT8xq?4kG)4^1uw@a=S_lA6q#NBY7CO98^ znTm&*<@Qte`!(e64+Ze?BtOorqxS8M&pcWj{FP4o?LQ1;5Xe=5$bY;H85UVrU4B<; zM*v?b%U~3)1WXTN9;k}VX8FHi921#NVle}Uk^uyOE=GWwa|APXE%XJpB|9UMT!i6k z!jq&Y8JEp=%_D@vq6?H9&@H&7o-1BjG26$MiKU=`RrTzt6S)}T8g}!Q)(J1&e;iOs z*aBCu=W5L-LSCIAsiQ-@*2O{{t98)V5bL=+ritE1qf(#0(%}u9^-jE;K0Q7WMXW=s zg83yAJs)%NP(u=yxLp{6Zk1vo7R-1qd_T>d`4yUe6h3{7h1M zv}>#m1Ib$3XfHXb8lTqS8Q3D#)>nBwgLtyhFpTj}t$6})@PUAJM>2}JX4aTW(AEsA zYduTzj47qRjQ4v6&SH6T%UFL}Wds3dMo7zKC>hY<-wmQWKg@O1(GzhlL$ozaGL!a z183ciMek@LW@ra2fR+yRZqOyAKzH$Bh2&~i-{``v)nT9E!^l{?<%c(C0C^tY9G=-6 z_ZJf;d{T9rS47nx&Gjv8+`4&k%3mn%jzwGh78a&2Z1->{tK>R;rmJtE^F;rQENw4H zAS&8aYd8KN(0gcb)HBx#|2;0(l#AjEMpj?m>FMk`6LapuVsKvbe!{Lu>}UOesG3Op zibDZ>Jfq)pDE;{L(Z?ZwB7sFw;8%bDsb7Bk`)@o=lJpbHQvp#5_tQI9@7=qz!Zk1q z03lhK@zw6mh0f(%M0-Y_grND-C}ta*n?u!`T`Mq=v2nDsyL~+C+TkbpqBqegr@Iov zei?vCZw=1$558w^uC=$(ZV4cl$DHh*_Mo@Sf-Fyl0*7$MczgHd*LVg~5x)xkV-YT* zh4J>TzP`RL9Gv})jg`RL=Pz$8oWP!f7PgA{!3P)~4;ICFMx=jvbd-g3=x+OOl1H@{ zXev}1?c&Mq&e753{s<@J4qO2FE_7HMS_{NhxF-^uRu{xF^}0d zl^)*QGzYairXqgScMoq)X}dUq3ahCJ&ewM9pICeF&E+cqS0RB@o00KgP!o=WG~TOm z2y)O@oY__E+bE=afMdvo+J_O~V~{`QexXg*i-x}+f&Bd|P38g%!{8tO`W4qtgD!)y zc9{y`YoCAQ+O@Z4k35T1R11j@Bd<+{4;U`ydo@$Ez!}VgW>Dyae7U!c&ySCj^5Wy3 ze1;KOfG0BC!wPo^0NE6OaJ;#BIA|1zNVKIuUz@>37TmqW#3R7wrwtn~ipiXbmf_)1 zO}DwyW*%CTMsCT)&8->9!4k7ONE&{`$=2a2X(X?7ot>>5Rm^e+S!yCp_LBeLH*qXUls z3x+2TVos?|YU!ors|T&5kFymcYac4L)=kg*w+RiXa4Czl(j@cjsoQ)Z%0!5?A~vMP zT#N})lSVm~LslFPfgg(mp2PU}O{aH{en0T!$Iw1d2m!)ESg1?uk07Czs zexy>`jsT*OiD?F4Y?l5NhZ_QKD~&t^RG$wJ3L&8Eng9EP^mSCz0|sZw3yz87mUbD@ zy4}Z-UL=1j*-w2`6o;K3hXAUDW{w39QUD7H0=YKILm;H4N8iKog(_7=KF$2KranNf zuy?Vghy{tHs_}c!B12%@XpDc+ga#;2f=b~Nt8)H$P>G7{JSdbhaA9Ld z(+EK?L>cU>FCkP_QMg$#5tMQi?pLZ~azJ#V3c9ZC4>ZMoEAcC}qExUc5&tbVjQbHl zQ4{$erxW^R5n(O(tfB>bH1!$G?((|jr#yX_FT_uNNU-+g0U|%4?e+>4Br_r3yn!!y zi79~eoChW!oKq{hD_lgSGz3)nVZWqb@<*06HbI`JDyMCoDl^X5h;4bWFF z-4$4OYE8`qO?);9 z-pDM?v?u!e+lPaN@8S9setaZts5{pH*6wF=gMBYwixf7h8FeZO_G)6Q2hxcqhq4?B zl;Tr!&L{Ady_>%Lb`w+E4$5hJB&ZhL5)^+lwcORZG=ehX{O|z&`&(Q)4Ew` z)<^yk2|N_Qz2r|az(x93$bLNbVervQP(V%uV$QMLni!+sy?b{hd*n!702ReI99%(c}N6jr}nP<;PKWN6Y0$-P5 zFuH(|9!V^v)}k@3V1lU{>FDaxQX&fc{l3)-6YfSNf#bBJf+auehhbhYl1QccqiGtw zmsPU_Q1feTiMgi5#U@W@|FF*j(kiZ44&}h^K+@CQoKdV-RA#szK<>q+I4IH9s|Bwl zaAuoyy~)!x!llTv&6SM|#*=P$VBj@tK{9G_7z3QyZV&dH*tj6*MC%J&E?3XYW>3+n zwGB;UeR@1Ty_%NLQP4YWLvv#-i%p@0wJFgx(bV+Kcv8pK$rSE@NoZC4k(;lHC*$#W zSFe*h=9?e3_?r@K?%fUr@b9o+ipIZhMA`4-ZtvTfUOe}vH(?MApAjwxEUzFC3!z{9 zp*O$a1s@t(IYJyDB~a}-xqJhCEk2!#WDB!4D3sc|aC^omjAB$Z4NSgHAar}r&PoXu zxnV8rXjdX=8HH$ktkJDn7IhW~NZ)11=p1SFG&Sj7Pj|#=t^?%4w&)2@l3leG%c%GL z82i&}efTubNE|>Ufb|dePsif{-P70J_Frqw{YU|%`WZ%Bzb9Oe=Jrl@b2T(`s4k!j z+`#X#=mwM(#mpzHq{iIBX!Ls4(kAC#LD`H6iEwv}wma=65YRQU0HWA)j#UMKB<;~? zGUBs0o|z5>95%<n*MwI6M^e*U@rJN7sHfUNeGenuSR?2o3BQ)M_3A{&}l+{-#M}?U7-##@h4ijP1bugMo`QauCA$d54?t6 z5p1e4+c^;iN~V^zY(6;z>()@7g%UT=*ZU%wygBj17!T`AvDI1!e%_^n~DrCqNy?^Q^E0X@silsvV+`9yL z{raWS^v3}z;ORFo7ree+b`>EMz;_A@;gwfoM~=AA>?5Eb5&lQ`)YX_G84l7qD($(D zXZA>?JeUHyUuvqUBK^|Vf~vVjMY4@bu>1JH*&4!IK;+{HnsvM95r8?WY8qW4R_MYBjlc zI*Xe*H(6HAFpOANGC&#xZcJ@>ZY4`!Jy)!6poZkedTjO*w1=FqG4h@kP}8{eezy#6SRksBuoYF zWTE0vmMdb_Q4L*OY}z0aQGt8NWJ?Q8jj+2TIUP}jXO#ld6=K6nP!I0|=IH^s5&;DH ztOl=(&a}#RT>8XuM-{C{NLrk@&?6~l2wV5nt8dnI5cO|wDy)0~j*OiADz;i%$CY+F z(W%DT6=)sbiG=4{NPJh6S|>XKQZ^ZNae4g|&xm{0l5o*Ej8Ai!Qu_S#4SgW6_~yCg z=-#3Fp#bi&0&pE=X}>;r{VP?#OHe?VZkCkFpys*lhFz_g^d zGypUg=v#3y7d(C|6s7=TE7&95!AkbP^m0hA$5wOif9nMiEK$z07WXpwP9W>HzwlbZK2M09>>)qjxk)-GI0~2Q#NJj_&^OrE$sHb z=1fLhp@GO9aFCNv0rZ{%DhC-TKLn7kADj|K;2Z53MwRx&6*;&Kl*ktE~lFSh|UWjTtS+yHo7V+U9Ocm8&P+nfPex!U4kTVZQnm0sHr|y;|RSbs>)zDxE7~- zCIpb6DbH+17El}A$8BQj0au9Q{Q@6QFP;zS4k#t~p$#>GX|eB7hi`c;0fZ`^?TnFA zm6Ldo$l)qX?*mRpqrVCEsIAX{GdMqGi){>Rzbl(l#eYWYw+^wyQk8lGc z3ITkM7=>WI@5-tSfg34+5>!5?It<2RMxj>D0+B0(NMLex3f%k|G>_Pi*E0{F;AFu^O{c(D18QU#Pm2nWGf4}G+Z^v_Cy2oO-u z*(;F-siXi>*GwdS%IQ)h6|uR!edmUr?7NfLW?Uv_4`VId=~zQ8_|2Xu-uIM1+A?A* ze&~!!#FOm8m83&D2BgVLV?`AY`-xBsIQ4K5!_?rwi8hx8^_*W01!@7WFa}r&vl4oO&Y`7Wgb0SWM_Y2pe zxL{x9793+3^MI2Hi*$&kg|XPxj?mDx#wov)TlQfps8h zV2y(4_pua;_u8|5&0NT8L}(RP4FfrtU3&YPRWk~UcslrGBiMlZhXe+k@(yql0)_?~ z{CAWL6Bai^I{cKvwdmLmxcG`H#=1iRT6)*jhaEL>W-Rv66y`ANs=;4Jtf?nEn_Zm3 z@@>B+0NsGrGPOK7UUoxZbk9m)W&>a{*I{mAAbw$76(YAivLGR#1DchDRR@}8hut|X z2?)UU!q7lGSzNfRZm3dC73K4!0lF34qH+{FntR%gU!JGa5%hDU6ISJ%0nrCe-P*M9 zRrLG7&EJStsy?e$*G}&PhKdgO0#2sJO{YvO3#h9W^A=kGR>S6}T{IxBhH=T$>cXBx z##{kE)twCRj?_SBFXN2zb;+vh%&Akiv-`O32P1%#!15Hpb&NsZazN{!_g)cwi3)gK zGQnlDB3iP*uYM;4aPjIj+;ji|Grdi=YCNx!HSI#j*jbKjPu#!X>r(xa1FldRF6eZ7 zs74Sn(T|KTo6DZSQV5}<$9oOGk8~0H{2*kl9$$#$L|?BpR-V1w;YGfZxfbto#7+TJ z;I%$rLxX*$JAiCw#=jo{#2HQMI1D-)Xx2kPRb1UKqOa9p3d>eS#H;TKWha1m+i-o4 z81S)u22Nc*>Enm<>y!FV-9MAHXbQ9#X-mbuEl8Fy z**pXCqwwq_U5kwWs@)y&?qS80H5Zc5?pmz3sl`2Mv^YNg==4BXDAV4B)u1ZO4523| z(aV=tGk^Cc9SY#18VLwT52pTY@2WV~#Bn*`IpmCUq`WJI6=>1|Kjzx$8H>{T2XFWqPY$`Va6vxQ)&;qhhdU7E(YzT2r>f1)TJYMgF zr**c`!tE(`dvk8g>$Z7AovY)9LI4C8V$LpmCI$wM$CulCG`T5Kv^zA~vv!*=+IY zHEVIS>BrRN(XrsYYql|yhp+l)Ql1HKN7A$0&S7YR6 zE$t&EYhjMApDUMnKvsgh5Jdbp-&@JD7LLu;nCM%?GO)!PIjk+SP|Fq;+Al0XtIAf> zA}LRjl^~=2^M#rb)i=D|-#XMA(=?N5%0kv=VD@P>+t(YUgi--BJtw;-Ct7p>^`o*; zFoG9XJJB&(n#Nj3G76c{`^bZ6m_%FxCm$A^b4xKXG3U|!%}aDt^N#f;Vr5nemv0)z zwvnM!SNB>ZC;^!&8(nr(2q&U|bmM~N>*EaU*_!Ha-wd)8PObo<_R*p4m)|_YQbbO- zvG9$n`DT0DqiNsnX#`aGrn+8!b9+a?b||w2=q#uQ6)-NIi6~0tOT)HyS8*L8SjY843zh@?qx^Q88Jm@QCxc-BNz=s0(h!udR3j<(DfE;}L zz|J7}EfyAG{fTAL1c{G!SLBa47*g>YW4*cDQXX zDG}a>074iVQ45^Q4b3((&a{Sa#?;Jee=2omxY1vBKr9Oc18ltlB=i)B4o}Hr7=;{* zt4wxZ9`gW_h57`)-x&mETNaX7Y%o^&!fb=ogI!ltb%E%D|5~^zvM*RNh!d4k?wFE? z9`e3&bv%+5JqRTQX;rxOI7`tqO)G3O%$J_w6Y+=fG!=8 z`q8a?8Xb6dA!9Uo-V3HWXqrJ9jGLb^PVS$AQli% zsgSNl1jbe?tJJ2pASf2RQ)Vdo|$EH=rN{RD`2P z%Y+R`0emiSsL#kZ3Sa>MsP5+p;+oltM+=~A5l8Ogh_XVk`z5>X#eRNt=aMbO-&Gp) zmE{olkxkuoq4NUx61q$M{hP;qcB|MP7j{npEZjWVzUzd_Dc=6y5J1L%r%xa70C?}U zFxTIOLHD|r`8W_n{N=v6xvR5{S=DF7CPr{1)ipQQIy8a>VtLFvc+?k`J39w+QR^sA;l}ap z?rCi7Z_db$6iTkdZ5>JD_u#C-G8E2e4f4;k`HFx!D*UQgu z{ck9stD5w!8An{y@)4-#&jks45JI#qUqU zig!J%>s-be<6BKI`SVLRY^Y?@J3PHpn4p#WFIRE<*7+G1R4t$E?LGU2kH7c5?|AQf zKmLVpeaO659kja?8MgoTL203X{{t`t41s@r;2Ds; zLqM(tBpelKkg|vYEgH-_U+|&3cJYOy{;jkUD%{}E=Xyaom0}8%xsNT{(9kzJx||9Z z!uot*+K=f07J^*8>(nfGdMboY$zAK}LJ1drkFYW&mKY5L9N?6UEzKJo2aBo1(r8i# zfVin|sLh^VN}Eo3bT78*C}qn=D9TKZKV zUnAhaH=-_v`V?QymA(3fPrvsaXq3S4HFKcgi*_c-Y^}8gLMNIts)=*dFld;+eCk%4 zUHNYf0skiia2*)@qx-0VJD3pg=v&?;CBf&)Vkw{{fG>FUE9dUo&9#7)v8wslsS z(;V$5yMYAbhuZjGV!DizdWRqri5>t?sX!nVWa~` zA8Z8Vs;&v{6;k%07+bKPAou{B_GPdTOf%ded>V~s)YSVwBYCB9Q-709XhC-vj}C(`m4w%K?e7!vlQbo4Zz?jt3M0;77-7 zPXp1i0aUlZ;BZC}ROPJ4i=YCn+7v)ESb~IrgJA>GC>K*TDW$qV7!3E^5kTnDbkYVq z44KR=?`dyzK>#r_g{IcyNOg6N0nh}gv=xXny|y|)V?a6%EHtZPo-}pgO2;BuC^jhA zV8q2;36u`l1ax1w7*Dk!eu37-U zA9o1XMWnKD_m%5JiiR`gy$}2X4gg(Mu9Yy(DzCs*LP$PQa$>DP19H`cV<7b<-XqEt z?Oz`~kFPpX%D~$pJfALrA94Q23i$Cml>(mCQQt*0G96N_k^hP);M9s_T&6XM;jZ2* zo&#p2@d$fg0NIW$!xHdMX}8RWSx*5M7HKcw{H49n5UdN}7gzvK+1L~$)TxyeA~PW1 zxjkAciHCBw3t)>r1s0U>jlggLQQCo8Zi!y-{!IbIboEHtm4uH4u;4HZ%mxXGFWIiu zk}kn12MHS_j47$kidGPb@xm4)Hdf5wxWjn*Z3rM|78W~l6$hzmWf*`wQa{XrE!t3r zWTVn^?7Zev=|q<(PDq;K6mb_{G0=gC2?4U%G@X#2BfG`92NIEDn7D1J6i zD|diWz!_kRdi1l0NrJ3$XR*XPVUCrK+ZjoW$7^j6Kqw%bVP)%Kf&$|yt4~LmuuyR& zz)EYSd?ntBgV;q|k*u~-x@HFz-xn#NEcm-b#mC##_YWQCX4C zbl4z(f&xf?A>p7&0NE6YD7Vg%apQc?i@1!cdquM)>-4qStWllm5539y1~E>ME$)LbrlmsXj)FKDO> z*lrVq#}enMom?&X6nUcc1dk&HwB#SZ*wVId1n^S=$hDnhtCFuK+!q`QX12Nl4&%DG zkX6@&9TEp^WD*bbRW;7kF|5VNAV=PbmtYa0FxYdMOB(`_ONsJr#W5{9S*A!#{-c&d zQJTK;!C5bDOxKYU!fkyY;P;__AOHBK_?1tQBS}Topj?RXwRBcpQ>0YAT9PV8ugrL1 z(P>gR!d@4^rb_@pXEp<70nD-l8qYYfc$fO&CiZ|!x}E`kk@XKXV5R{!cMQf0tT6ks zy2pT3ra*o5Of(+VH0(!?_g*dHeSm`Kk9}9Ol8-hl5Q~gHCk6G!cp{)_TD<386)7DJ zpOe_1q5@QpZ7jvGKN6^E-rHUt58C0%lZcjwnscFkP(bh0xu_O|T3O=dCy5@2o@A+R zrhl9~z^%hw<-B8;?C1Pdof-_-VQJQdZM=&9KCd;#TP{KY^M;2Yiez7mj)&1RycL?k zDP4b?W8`P8U0q%7(_`&k(I_p$RUp>d<&L^*#`?IAkp;`Zqbvze?;#REOsqRfJG*+%)YOKHwyv~@km@|7ng`4(fH^w_@H2Z$0Jm)d%s{TPEP<&y z@Le5_Z+?ehLgIhn$KU$aJ9lmp5?Wv!44O#?yWH-0vfi&KB99=kFLdI>iE#Kt2ysnG z0ipdJjScR2v|;iDN{f1k-Nk$oM_r0kAdSS5?r1dLfKf@h#0xLpvRL_dF@DdrMUd+0 zY%Iase)WBKZ&$qGXA#<(%LoLof9k#d8_{@IvYvXjbY}|D-%(!Uj>kKb;UXS;67{0g zRh_$WBN~0TcG@qd*bQ~vBO{SWSqM?qw_o%xg`=ICnw|#uU`uQOUv~5r4}`Ghi0LY)haq2fnqmNcKp3z#M5Ak*!{D}?0npT$@^3!80PYTAj$LJ1@73xVgHKG6<(w7>**-kt_l{q*!y z_jd4sr+X@l0jP+IXY0{k@7bv^#+riFL*vns7cWkuzsLje^WtCq(RBk;)8VOZ?)p#v zdgpMM8%dIJAQ>Z*V-GI0^eW<+VffPP6u<2UbjAfSDX3R?J7G-NUDFypK6O!8X@lH zH|HPrwx=i-a{`#N#|5zI(ygY9YQQXj*_OZ`ruu=Q zfMDaBpNnts0FN;Zi$kEGlnGk0i^%!MptqcVh*A~#n^*kjy#b;jhiT~G)?iHm?)D+` zbe95%zwBhpiX8t~0Mq{k|L!<3BPv(=k?Gh68Q@-Wg`-4kVlh(^Dg-d=68O#3BH#j5 zLCn7h$etjOzt3zFK*a8*i|LVJkcitENbNAEm*9Gv_}Qt$NA-OD9v>LhXSbUvbb#sZ<{fjvf1>%5F)$KvxlabvqJz6(e32h zWi0#gMM{Acr5(mf`XMnVOVW}uFQM&rX1z6>0g?i~;tITnfT)-N&(5bZz~5vbfLWJ7 zV-av}i5CIQ-wgr*{N3ja{X2rttuYA;EwZ=~WL;k_HzuqY8=Us(9Rg6Vl*=!wf@wQO zLF6!2+A#+7@gx~34(T+&&KzFJ7#9$N!dH>gc#A*`+V^M6$T zaEbUjkgaIhOl<7JK%Lj9gNt;~Gk}9t#eNBa^N^)|1+}I2nb_!bvA*H?5CY;U)rDrN zsyezII4EUfX^py0%EDu#qXT|Lpc`bu+w;0F@UgulfQX4%%sx4OpMd~oT>^h^yql%F zTm`vgcKu@k{5%DALGwNs=HmAa)W>F~%OsARHR7FUQr%QlRaMB( z`|wzfP~=svO~+!fP=p_yBN|Mk?&;AO2B6L6WVK?E^t8{6UN~{mAqvbM*0JnG?cu6e zxY&`S2W(7VP~Xp;*dBk_NE=gyrIBgG>& z@Bohp_@5G*92^b~ceVa3qDZ11Hc>g**w9&1JB@wJUMQnP+UCCi%7`3y*LL72l!;O@ zsV|1!U0HS~>y;x1Ji7cTk=S6DY#knKqJTDF3?%~>FA_E;-Vjy@+z2A7qoXyT*{yj& zA2y;(*~kEMog#Bcyer2M7Dc-1={J0AtN2 z@my3uTA}&C=;Z#aO7@i#ln{_`L2$0rK>vx#Tmixc&!qO+UfBx5i`<(8wZ!mV>S*Yd z?RiIm+Nu*DWTANTmUa!n%F7=|0)v!%)SKv$tVMp0N$E6yZ)t! z(BJyj*M?hJ0CRIILp44-zy&23F4Sa3zpzX?DzUnb1Cpeb--HASwE1|rR>W2>XKkd0U_={IBACV!aV7mW_1C?1!{xESHo@k5~qNs zTx9=mg`l*_R-%L*Wk$?UnAU2&_)=2&OC|q!z8Y;kNkt}SMQsB2EgucqM0&n*O`<%% zqE!Ri0*}Sxtv!u?@!!;BYD64$TU-R`K7Q0v2zIyAjN4;$0o+~%-aEa(lIj%ojlfEEv2AdvyF7ob6B8k21LmA{4E%ak^)#zfo-gWCXv- z_UdF2!0hGg7!-Em|Ee(WZ7oA0=y>cOAqVbgD=OWBb^4Io& z=11tJyf5a-vKvx%MdpRINV@8hG0Q@gs zh1pxGf^5oO`~O-9%BgA#r+O|hD#mYwD`{o6?&|nOO4ajK)iN9>AgzS2wAKVJ9#Uu^ zrRDSI%5b9qh-r}X?Ffs7a6ILG2eKi6afYN?2nGc5H_^;Y7kgU(F$ZqiqCx*Y>jKCU znBkMoEx{li?ZC4p=i=`aKrA8+xM{`$$R@&`IAzP_CBWEqm81@G6%4dih=(K_1Q2xz zJFA*2=yQkJh7ztic6&ZXAWx#)Ap(^;{Yj0j-h!alVPaftW1!z@&046cL1xzlPgk%2 zo*dLLTJV&GaQ2at5n5`r4V-E5(X@o-j}WQgpV~38707Pa5sUg{(5!R;911JcLFXuq<7dhM zkLO!R0I>_g=_rpE;3!6#6+o=hCn;CN6pZGuYVh!9p=;V{*nfzqG@r(@Af0PP#;BDO z&{%)~j>~pK05LBk$_X)6Wjh^2*fp~H_`d+MTXKCg$k{jOVJf)=d+Dacq8Hi!!>R_= zKy8IckWxouRJLHH17Azl`1$wzA_-tcc|cWt`7~D!%GayB|Gog`cwrTnhnq}EWiW8M zTQps(j=t+(;CKT(Ksbq~WBy>%t;NN;T|RPQaiNI?5NmC>GB(770A^nTjc(xCrMoMe zYilp={>vL-=5aMV+hLIA#;bnAZj> zy)_t=?W{qki<_B~A9KsV43hvJ#}pWxQ$Yl1y=%CDO=wFBo%T_*pbE)3h#Dw$ObF!$_<c(7%<51Iuq`x0mf;KS4>dh+;R zyDTs8;Z}BWURKrszxai(f9=ONzYJq^MC2Xp+hP-QxUDH7W6>q-FNIAUFK z7*No#bjKokCtY)-(aqanDA(fw$mS!pffm(@1hhnMIMG~5BS=z0?~nU@`LGX>wVDoY zXZ_#Z1O18s+Ms}O4K4*xwX;w)+&zLZhWmTepe*+codW1y0!B>2mg%+zS=BV3*4ft0 zI8&Uel#MlLLF^YC9s~~8NwGW9XDF_`FmH|MN@7AeB%Q6@QEIeR(2tPcON`K zrifU3^5o^bUIdw)J@}s&KrS-xQUHdXVmARYfaE$f>u-Nr0lCj%uo^TI0!+ z1YtMqLuz`ESp+qADU}-qM5WC?T7yQAi@rqvSdr0mfbA`fsQDgu!Dg-svn6ID2x9xy zGXX4LY3|G6BFHbN5DvieTUumn&AuG=?j`#0e>4O^RN7<7!NG>c@HO#($7s8j2+7@W zI@vbRXW+caY``0uynOE5`;#F*zU98c92ZIQ7PsNywN)byvO~xUyI(1bwc>TIOkXoA zr|}I7;PQ)GsWre|2;k#oWQxWz86bdc2{Q5D62NcnP5_(UdTV3x*8IW-6mZSV0{GQ0 z{o$A2yMw6juzgzZ1QDEdQ1M^$I28%F=z^+=j=@F>C@+G$J#vy{vSp#kksq1tE=0d zy`k@#?j9{PFm^)c(So1d=jro^(jnGA4y5J|hu<$LS+kGA3o8CP1m~Ehj5jQR51*!g zd_@3f=io1{PkND4;fG7Q06u(cd2^c`$i&3lg4qOcd6PB*-hHsKcHgLKfR2!Uo>~4l z=`iN-O>@{hvn7C!4^G^=^`G2G!!#HWp}+k3V+frHBcOXz#1L@MV9D7DrDE+$fn1Oo zdcAdZMSf{JBGc1{-*&$urWtY6W#C6Rs90gZWe^IV=_PbiI@!SZMM#wBK>$<3*F$64 zi|Lyp8UvVL2ao^1{O#cW85hj+x9N>IC5>?uXyh}_K27uEd(0@9>;!wrk)47${g6y6 zfD4-sonZTY z-34rfo;Rw%_g7y&dj4STt}%;WK14PqLiU!CmED<5#pPd*@QCGknRlXhM)YJoVb|8xpX~@1Y{v<;%cT%NsW(ua2 zvyFBl8tnHpMOFI$%HQijZM|Nv^vYc`E|DRCJAtS>Y0mnYV)nX}5$5KUz3D0N${a`2 zD+2iRP3+GRjN45nW_jfhj;XR_3^^|CC1`UY(9C5 zr3L8-J)momd>9*m!WpGMM@VsI%;|4TLnsO;;dZF~Kx!2q<>wlJNlk zN}&t$pR3iHcB{Gxw!thfr3G+tbz=isaqA_1nkSKXw0N*-bA5B^*8Mdo#Ol*$3x)tb zL;m-2W#I>=+6X;QV$JdLK4!d-vGsX;8hkd?t?#dIY&?Sw($OYe03U3=m|LN#{nNRH zwTE<^1n|+y9G6ibld1U63Sim~BsFlhOOY;HJgRbDH|NPXcwWrR>FrfRQQUbFAKvc% zmI@&~s52Hoj{U_?pbLX~M;{hU(_T@uKS^;=4TNW;`n5u}i|@7$xRughK#Hf%PpySn z4^cF+VSx#@!Mt4DRs@-Yzxez*M1iGldHvZUF9(w1Ewd!B0P-5*vx)gdbG5=feX;%= zxhmrM!ofAl{BEIUy+-eqX))v*N1t?-1#knOtZ5^a>us)XK0^k`xi8Yi4C=^P5kOWz zPoLlK6-2~q0?2>k43Rw%|8)TrL8_CAJazn7ovE+aHv9;W$Onu9bgBq~(+qe!r!+Vd zH4wq5wq*MIE>g8c6vzP2oMZA|8^=mneQ7dTJ2r(&!}pA+%t~3fG6PhOl&Z@#*HqGRxQo&0`-S zXeAJDePQk8lZQ|6**Lr9a&ObYdHNttuWf@_d;NX<9FW31NVi9~hF`FjI4T>zcH*K2Cf*D_ukM$J770(hvdBH}lquCBYz ztXl!!XchSWYgJ&RM-WEn2Mb`+i|2Z4#q;}rQvl&Vv}7tA)~)+XOoLgY^o|8^*$BM# zyDyE;wl%${#BcsNPBnu79s%e%s>vl@^oMGzcTSXlwM~?crk(yyXUuy zXwXVr3|i%A!v%*^k+?%u#O~-GO$LoRd4e$e5|a=5usd4wwIs0NT5l!5nbOHd40Nq! z+zp=#At$3*qSsmU&LW0>6q8Y&_z5s$Y|fpPwGWPral6%h6D#^&Mzcz}zL zxefk^n~zsk9#bq#(2uK+pKU%$3E=ZZ`h>JVIKlJhi8m#HTmI5OP{4dt2*+Cf0%SS7mgQZhat~I)G5O^6? z>qOJAjSg3qHAa14->ddubwdcy89wu5c>s-gXod4dlc5aL3d#BFi$DM9```cmx4-}W zk3RbN_AxU;I{&rkzMSxZ6J}R^EoxVr;|S&JwvnXF?ZF_^`})HUopD=qdfZAxS{MF| zhnzW-DcbG~0=z?Ew`&dfV#vZ2c%~O_2!QDxJ)RfThKijg?b>;x0=P0ir^kdLfU_Hr z1xkl_4X|l$;r=|bLQ3{#A%KvCv;dM+ZqCl81n^I97K#5W{b3JKz#t&%8c%^=qXALl|@4U7ambUwX&H~tT$_l^b7rg&+*pQH`g|3OUu&1xtH`7pDrPvU0!X%Vjtvj zVQF>w6#+ynJ;BLqD=%pBzxn0_(BGP{GQ1f?D<5bGf?kS1)?}L`0aPSK-gLak5fP^15xSYIx9{GtQ5G{CGim(TQrZfmgL#<)qE9Mq#8&% zvLS%ZkxoX@&Yb25{pRguyc8FL{ZG>LIaYU1u zY%%d6jCe@fn4do9Qa9B?p7HeF_-yGdK8U6l2116aI&V$@+t0KB$;Yk|77`{50rF5h z+jwfSA)viGIoSwA7D5to!`C${1#(=iCVD?z$5q2+tIgo^R zF#|^Srwic4k^PbZ3f`;Zw0=P}vi!q^?h{TYSrVMm?8bLmKA9Kf(#3K*E(fee1mh!g z`EdOQt%vg=e?A|MCXyA|5J16J-zyhbK`v{#5wm|CsV69(50(T@T>z750qhCs0$7nODIkw4pN!KJ zrT%OPAfC0nXSl=ymiT)P(X{2JHzt60S6+!*2Pf{YtSnLc*wWhSDkw3JSDwyNBDA@> zzWQPTDna`4cyn#@e$(R0%Ck!(9P=w{>+36-mVrOay8NX*k?t?iTUuXRnY+}a1H10i z2UwYBvP09;wbiwidAxt%Qt{_g9Ojr?!H3rA3Aa!)nZu`@eYUc)3!qk(GC-%(dG>8x z0FSZ&wmEp~?19Q~e>^@sEX%{u{!nVmOLHXIQ&LdiviKS>0nVWv&AXohm(|L68UDv& z@s+axA_GjwSib_*9T^}Xt4@I+{WS?$J(@=XXbpBXqBsoBe%S4Ur!7x`0ol5+YXRiV z!Id{z0QLC(&oAyD0aT`HG_H3bF)xUh_X#<;Pn9uzThP^V;YtTeBfRUgjN)hm2>u5HVrLMbg zB$y!}~V2MRa9fdHp=WWKZw2XAXZA%-L^4$&H*gf%EfW3LR#G%U&B7JTD^ zLN6jlt|aIk@w#C3P}rf6eNuaWpd{}wTP*=7IFN20-rbP=4y^$qcnUmx)2#Ed%){Rp zknJv7n6PV|9f|-v79fkY0hRpw@Q*eGw{&KAJ6fw*0CD|&=R>Hid%TsEq5gmj^fzi# zz5wL&NOtz-fG6&=+v8PUQ#Ku%Q2;ZPOSr<#E~S7~83!)5 zAPZoc1w+3F(!fwc29q9ngxphJfgFsb1>?xpcK-SaXKoGy2&kNTNGK}DaFcsV9hp-y zE8CMAYJ-%lSQD)w?mM7tPm_b>$q2LcWLc%|batU%E(r$hFpBdfYWox$swK3EMusW+ z@#7X(i+ik2$!tU(yYmSPAZEc3z;|q>%uN2J5W8ywltg1Ev^&-(=;#O~)LN}F{$B0` z=vk3yy%j|et)!#`1@@A_y%5*dB^V;qRQpH(3j@hwDNDP9AhyXK-*~y-oZmBhUjRV_ zoSj`fm|-{Yhgp`tbSH4r-K8ZGKuQ2F9W-;iCV=1sJI00rY9Xzz^|po#mI#N^c>PRR z5U7)i1@I8uKH7)IebqIQToFeyL^fd`jK*&1YNDndSqUHYUg)nzy2pMbp~^}fOj0-2 zsi{Z|t+eG2L{td@_X@X6#l_=lytGWg)HahBKXy~*eSoZg*as-=n~i`3ga*)C*4*#U zY_$W6-r~tFc^IjBZ5XYCbW{Q+KUUustHMUks?pIp#zv#&?mv-q$5rd_VB<-u_GgJw z?JOP&^oLA(jrY6&HX&x`=Wk`W2>4-^RO)`I3;96NO?Hb9u#95w)hur}rrVLlE@xm8=qU(RK&Ru%y;_HmF^+}9Qx~dgERBW0U+15hnW{vJaqvRO zXc`rCB;d91=m(86?S0HDWCav#Xa;V4?}Hf(>I3ZDlu0YF;wT!qJ=rk$&h0xli@XBL z$_MsMFb-PsZgE#WWgx|nJ0m0=dc_)u(7|V@&E}+u7haFz-D>N}0`-TM9P4JT9q^bF zh`$d6kc}~akRcWS!z{~RdKVzifEVUKgEn{m_qPNvM<8m42q=NTVneZ=S4sni1yCO_ zQ|}MtGOj9992IeyU{v*=ELmDU-}s*mW(Apssfu^0Z9Nq_sTYCioXvx{hPR#L)Sdn?(P&hUO$xG zflg}szp8tGW$#>m+PI=HPVUr^2it?+nz*qoyY-9KHlbKRFt&NW5m-RNBM5;&Y*1Pv zxq{F(5=k1V&=R38(nX|76-_p2mt8cANZEE*{(~+mb<;)vfPUwC9P@0M#3tr(LP!{F z3}fc!bMCq4`vi&{jMVszAYN0Zur#y3(*i(rL7<4Gt_J?Gu=vYYz^gB|9&;_2nNn{8 zs{!DCqX#7i zJv$Jk8ck`u{G5CM0y4|3v=dMX6#y(C{-_7cWAoYL>@L98Qr`xsl)$P0nEl*4c>vN! zg$5wo13;!+LsXU*Aa-84XN`S3)-&iVJNCD1X@x;7W4j#z0kz#!Lol95?@iUYtQx=V zf7yc&02zJVxBnEJd;n4jOdDZxxcO5Z02TZdtO9-p0Im}N(F88>I0S$PF|9m%9n`Zu z8K4ry%>bE5Oyostp#Z3%hY#Ze~6!6je%@I3~&`0;PMI006FjWhT~4va8+HKAqyuB#Vec?vWj%1 z<`;%zVR831ic~)ix}67*>~(?{z|Pphe0S;$+G?VkfdmZA{-#8)-QL^L8HC6gPv|U` zwqWFTb8qiNZ6wWDHd8CZ#+uHK(b3VNq~DDj@-&cL7JcIrqxR98b3U^`2*nv?%;39i z_e}pVn)I8Q^H6JH0M!y6p$BiAmeqym2`GRk$@1-K6W&78n4aIOT_?2{Q8u4%Ln_DWtC*~9#FaYkchfP&8yMmC4fhMe>pYIr?VoGL)t~6>c2cMLEJ2V1kk#Lq zvrAT}@pz-PRR#-zfJSj@?Cmz4Bt`8HV|;ffl0I`!$C%D*s_2quTXN5eg;b^`2@@xt z<|ea2Y0DcL?CqH1maye|JZ#vHFvZ$RG+4r&(dqy~@+5tjc%)4^EIsR@Si&f>ZPEW< zg8b(I+`~WWg3PR~uc>N-JpJdZJpKyS0?B}XkJnc>GMVKPZvpQEAVLA)p3I72LZi_m zL;Td`YHo5HidUcr-E)0>I;+}V6Qs2yI_C6sgm9_ZOp?9Rt!Ux{Sc0MLli9nsjDq?U zPIS(HW{1csl#ioHU;X8>Gt~wG0LwKjI2V+dd0*SISv>G^<68yW2wlHC}!W!2N9yN&(l@rGWV;WD8{D#THe- zd=BoUWq^kQ(C8!kjsS$Kyw%rw43=UMP)H}CFQc=0t1xW9qX-mzJ(ntxZl`x3H2`X(~p%gJAU|eI|Y2($%Y1 zLBPRC8v~axVeOGgHcN|;NBz7wW$xsFP79UPimpnX)azr^Jo(^3V!#9y3mZvh$Q{=W z0^>eQvJD8s{dL~b8imz2}?rAoT9)`Yy$FPvw9 zs)9DcY_;Z7z~hdu6k(bzWPqD|7j)?-fQJGQu@g&{y4n!3G95*AAEc{_^y-WfYS0$Y zWkYTrX)}^j5;U4z&@8E>3vPPUCm@62i}*0bj0|`>>tKzk3V<2_u%{PHCDlBwNrH98 zDN;i?Dgc9X&||b(iGX?&G_sfgJ+fHB1)_10TD>kb<>Y(ehlk)3fQ6)9km5E z7m(rVLEUr1h#jvWIH1$Pt7>u5Yyqw;Jt3R^YUNd}l`3OT2HHRDgcdbe!I*C z)x80$Zfx*pbFlyzY>Yz+=6xU#g^+|)6X~AQ-$?FBD2%9JovsP;Z5&R+CqKmFHj`~> zseN!n&8j5T&UcbB+#sz~!I{P+Sk0X&03ry0SlF|f^i%?SX&C@qGE`lYB@;ql3BRd< zWY8kQ3Z|!5A~Ejw;g$rWka4~MB8*VC0|zNw_a+S%YJ&{lZ`X37qLS6!`xIh62V1gO z+$)Q+w(`>ZQ0O8#T{x0}$(AsQxmr1DZFYaz;c*VYgHk|hgQ!gb3*G`=eerB7CxmbU zYT*3oL8=+fA{JVq; z3q~P~R`H;)l)QHB>oMu;YZFPI!Ep3xUSy9fow#DFutJL(*Q!ox9zI*!0r{B<&&gg` zt$8003Hp1B9*{#RHj70>gs4?kGZTkAp|?(v<5_J zNdPThsNYj#c%t3bRf(fuqb<~FW<)>@bGXCd@u;Wsf!VD}%K#k|Zb^so3m2f)YLz-- zX9<4@hLzRu2(o%Iv?BY{*#RtK8V*30 z4gvm|T=s>s!{Z!)2M~~UL$BpiK#YSNWBi#<0Qn}s)fps!CEo)+Y6d78(kswZ2LQd; zpE8hy{%CQ6e1XxNtpEY_0VukY39)e5T*EuIolJ&0BT5yM~FQH!Y-kRg-=LJT0o#XTbJ~>Y?SJGC3_@{J_M&# zhsUe82K3O*zyFz;F_7|Ny&qDz0kTEaN_e6Ww3Yfnt;a@Z)oY6n`7FXA4Hz;-ji7|) z0JM3M*Rh9&yAlt{)Me9|bhh^U=Kxe20AMDwv7Lj?H%Cy0E_4T^_3?{r3j{(47fwI| zcqjn(f(gKwmULq%cyuxnz@o{AX5A}zC$M^O+-xBLGNbP%&o;FB_bDs9NEfBRH|o^i zn*CwQ!9-Ej*6epsgQXu0N_q+_w!z#mursv}Fx!iDcDHax7j%JYgGMoz>vx1)6%|WM z@!rWgywp&;iJ7YsQ7>FsY*O!JzkxAEeI%p5(Sv#b3nSN>%zOy~wXNYnO`pvg@VY#= zyAS&%Api>X=^DF)7K~T+B&%pACuM+biN3KhvfheDuZ#|t9UkWZJTL`B8w8Eut+&tz z`Dw=$WXgSy4nW#)h^ya3I{O2Vp0=DZT`aEV$m;teiEDj**J_&T zXt+ni_!AK8?68OIL&*g<6Zm3-!vXc}2pw#k>q&`pBL}U)8n3r>e2?**7G)HUE3ab0 zZ9Fm5_|Wf$&cb0CI3F5u-?}|y$K%}MqdX{qajd_p%BMs(?yVw<0KgAV761Vo5C;Rn zTWc3oXyX^O0M2Z^z!b>FDiT1f0G9k9@Ie5)oU4J^9x^%RCo#K;tHtLONJptiA&C8- z-X6l^boj%s>RQHPpM6UQ=a75~V(!tIB4Es3&phapheP&IZz57>JUxyeN9P}-(<-{> zdxrbngs%#+^AY;4K7Z2dm0nik75HSD5><8dc#q~?*G#@VjKU!{GE=J5y<@q^M!0iji zxEJQnR|W8k>>fb!14cDo#Q5m#08oKa@r9IW1u~y#?A1ywECBI?oIwEQf96BCs_YDh zn9l$Oev5o%{92}Q%I!)62BjAn2a=4h3oN{Dgg*|is)9yQcl90rW_ZbGj-=q zEJzUx#$vJlexty5cBg~xU^*3a*VUzCPTmR?!m;jHth*bx0M}k8jKiA?#1i4n(74*T$=i7S-D%M5FuG%IrP+&4}A#DJ%b2 z!&vIk-S5Be>+7@gn0*wcorDJWqq}x{2$J}ty|Z_R1%Yq3{&sd0H;LL2_aQZ1FKEs@ z0MQ~cSj-K5)!IrZRq3L)eaeZp)U}X8P#{n(+du1eQd%uAAw3sbs(`?Rt3C8Eh|^b2 zPsxqMfXhUh3od)aZ7eIrI7JyC-Y5m!+B^mV=J~hr9Q?yB7@h#)t=N${-Zenyt(CdC zxx|-faI4!_w1)q7+K@v+RcTh+UR++Dmw*;y*k}8y#HG4?_1O+5-*E%5^*B<%C!5!HcCOubiZ%Agq%)lXMiRfljV{U@1U#!!UC!o1h3st`dD6KBjWy z#9(e&bdho~?p|xQKsuT%eQG?CR}1MYVA0cwsa1kq@MgP7TVX|b<@=BmDyc99x#i?-;n;*ns`dtGYTM=+^&4pwq24#-s?ch$ za#Ijjv(HKV6#b*^?XD#&jzQoedFRXn@WMx#Z;B9~^LL-)4gYMcqY<)NWCEi9p~TvY z=PyYI5Hbi*0>tCfTM;AEddyv5RzEilL3+%?LzWr(p@M^!eAw`;EubYE8?DYhQV4+r z0qXZ-5Ewx~6=ZSJ$wXCmxAej0+n7W7cNz&f4%QzIiszp4AvE|V2nW)%>W9`#vDRC(uEH$t}-E3#jC zW(}eU8_(DB>yAa(4fEqG{$(D6ey_aHTOov1EtKv<5de^}bWNxo`Z7>6 zBl{%eiY1u>mX)1`NsugBp+b-$m*9<;AVtx3ME{xJ*SJXgqE@{bxQA?f_7gcHRGXnsK zs#Xu}etGdT0FYcA1;{MPra%B$6Xc&R5kG>ij#b-<)<{$tnXOG2dCGZak9I-AG34?#4;rhgYE`Y`c zK`}nt)5ZvZD3-llT~~>V0eEG?6NGyzwUepURZ`lIPuwZh7naikz}!sm+UnZs`g6dj z7z=^a{>bNlq=B{00f_AHBshT_3P5%U06_!8^$w5CYibWQ8H^$q{Qy_&Iw)cWv@TbC zwn``j03XcB`bz=WZ9}J1hWfoa3wBYMzq=@9+cJ{<2K9D%5iA%ZgOGUdgK)9~Fk!50ifb7#n1;T5BNx zq=f7x15=l4uqNiDlg}0Pqu?L9T2kn<{*rxRIYj^@1`>?W2-!d{xX?-9Q#fGe`ybD; zBG3TfOopuKP7?kO2jJd$ScW(A!)aH1)VFgwBiz|5FLin2vy)+=AOIBHAec^qFOTcW z>?dJ|Q|0bz!Q5n*ard|#*`p%dg?A1^)!iQ^>MjamZmcCev50=L$%bY8xTi_TKZX#w zH(2W~)imY!f&iF1_t@{==yB=BqO-{=4E<1VhyQSf@Ds6FWu7;eb8qJHhyI9MCXwqB1xf zX@|o|2VdzX2nMqolKAzzhR5-&rc!A~St;x6oF)Kr3~`Snw1PJ_w|9N=x2JD_zkKz} zEs&n^{eQ$ibbx99%Sj{PVWvpC0jLNWvGk(_(kJ*hPbAU}X7xfrRZ0cX;Z8Z7$`FY9 z0b^mGN1TFD&~K+4CxX?poc4SE2Ns=W=dlBTAAgJ|yqrGb=PF+xa|aMc27RQOqj`1m z%fwgm8Ra?K7FO~)W2Xs#z)-FTUc)-z`t#3sefF!Tg}@)SKU4mf-Tya}Aqo7GV1x7q z04ys*uC#l1Fv~ef6ws{#*AwHCR_XzG$Xx(F{6L(4IReuA{6`#uJ8pd*`*fZ)R0Yt9 z+O4Y31W~!wZh_Qt`ruU!%&4bS;>r@I3V_*82+1;MR*8Tw*LQ#Rw;$gSe@glLIJf_0 z8PR$ooPbpWa5pr{_rOI^t`WR16aazPy$4l=0=Iqn41L{w!Q0H59etg10faY*Eb_9H z6p#$^_hly|P8|Sw7Kmj)PQb^sBXIXx;M3P`eIRDmDgEOPAiV!e4oD}j0NxA0%a^eP zNPjhNd$mTR*{!gyx(41e&@XVJ<4xS-b9TlIfsZ4|QYl*j698$Vx;%@Q(q36TWdJ1l z;SX#=Xee%VohN~>n+E>)+n&3wZejILv4Erg2X;T7v>kw@3_x0F^gV01@KIgxzJcx#IVe!=+2WQUhS?awemw-@R^plfj)yB9QZM6Ee}y zw+|lF{q?the*fc-r%#on@97^u{{GKj`5k||_rAP*$+y1{0f9gW|Izn}2P;JUzmm-9 z1|T!WT6&`r#1N$BsWhXF%c>)_@X3OM@i8u?r-D3ke)7>RxQvLLe*k1~WidLY^Lk0) z>z=>9>^$B$%8>z9bx0(eP@!{OY4kY+Y#%h%o137=s3X}(m*;kOsj6X>8~`(`h4hMh z0{#Jj&z?Qs#)94JQ+fS40^-k{fZPDCkoxzlka|Aevz-TEo`CEv0#Iq*jQb=$(jlK>SX-=4cKv$@xWTy;E3VRb0JA4FE+^6oPjbXK!5O zKLFo)hW{81RilfEpv7P$*;c@=zAnMV5VTyk#Ah*_1F!(y-};`}xV2JC;-@-qys?Vc zyAHpd1F%wCS>c-gHg*ocBgJxoT|x+gAmHPzt!LXWUv90cSCwDe`sEh@kmGNQQEPJZ7yhXm+FZ= z#Q@}K+N&gx5V*blVnY=IXHfehDEs5vUmoZ1_oP(qj^02JRik-p0Q5DtS5$K8Ijz20 z9yhm%8Z`j4xF3z$LoR77Y9C!m3#fO?KYph^ogD0wAhPwv*kB6)NH=W@LJ$=IL|7$z zmEjzKh1kUIY_L}&`_o^#lm(y>O(!ut<$)r!UZ;idj!o`82jJ176(w+CgAF>|b|B9K7rAa*3S1N$aGLM##z0wgY+IdFj!-1rB$AOT1I0KQk<4ki#F zArbbZzj2&#O?Qu`JN29QYWW@jDE0pVuoUwL6kl=(T_yqZ{bQBp)z$o%Wh9Y88%F)j z30h5vvsrd-WoQVMMtoXgXQn27+Alo-;JgdK(*t0^#2g4j#rEpgrU`(a8VZHTQ4Ok{ zl^9Lwp3dBA*kJ&?`r(%!zvA)#t}7gn;&-pSBC$o&!89H6`Bz_k`Tcv>{1b?u`W*Bl z{C$PFUwiW#@P})?a>e6#$!njUUVgX(0Qmn!fMC^Z4B^6+iUM3cGkv@wvVyDm!gJ4k zc4bC+>A5@KiFhKiTe9ebjmBrM`CfZ>znrcyE&({aO8}fl0BBfdD&+F-6eM_Rv_#{5 zsLFEX5GuMbDjn!at(O!`fdmlQEBCxJ;qi3lR>KYhpxnxPdV)0;y+01$K3WxxRWZat zfbc6C5WMEE7gE3Q`u8YzX@d{wa~8hH%9l^Q4EvW;*KsCi&e#x@{VxFI@#%!t2kYT0 zsoLz*izf)osDRzhr&%Foq ze-&rJf;pQ+Tt&FC1Mh}3kp?F{*;8&pf`W+GB0`Py(HS{9GW?+?k0&NaT3d(p8 zh0Q{LGMZON!OPbaTW@{Vdb;*zCQzIXH+fhkSTR{NW(b4|;)o{{TQB@Zp*1OCYl$CuyPeP>wZg7-+ViAO)WDN!m}rCjc&kM3{*csdIKSuOkEoS8GtrLpu{ z&2!gXauH`}ZE_B)&Wp{lLA7J~X9Mu^ix_~H$cnuWQ^`003?y@+K!RY4f)4ZO4e!*5 zKYJ0!fR@tr9y@<=Ixd4tbyM}X;{uTVKVyLCLwai8%;k#3t(~{|^()9r8Gu8b-B-w@5 zSKR^F8qRJ6Q{+0@}EgebOHFEWjk(r zJ-W~%QTmKuBxx2+Pvl=3VzoTF+jJB(T>zc{fe%v@NCMB`7%k!U3JH;(tyky@huj^jez-f$EF-7&zEjk3YM8831Re1RB9Us4q9NgCR3B>LkAATm9*^WJN^ zs`UAZ9HF6e77e+Zflrf!hXE*y$NUL^NBPlu4+HQBb6gjI|IIEH7{r{f3dL zhBDc8fGB4?DE7lqH0Ocj_aX}ZTmZTNJY`Aj9ikky(3fmK3_!*h|H}xF7@Oog_NO-Hp*nO2bNC0W^!!;a0RI~U<#$@rBGE>)qme!Ygf!PdN%8~ul924uk{>4k zpc?_YI;m)SUBn0PzWeTtiS~ON3RX7mzWa`iiO3(EZ~nrJsbkM06YZ|-~cva;KP8POn32t-iUlo*4wq7_a_9Px5F#gKrR3~Mm85xkIN{@K9xX z*EFAb_RYNoI5$$gVLJ5s{i$X#XU;V$p0kpdvGX+s*akp|Fi4A}TQeMZ0gCpAix+-> z3BYq1{<%6Gadqv6Yb^s-2u5+K5GPL!;$Nk_YQP>w(X3lW)|l7k-}Pm)j@Ce|w7wZs zx!j+qZqFqwLxE{bVtgYi%a{sYOU;#xq=M5E;|n!sg4qS&sZ;P^kxc8iU0LPL1)!_{ zO+r^k%z84MfuW;PzjiXnY=ep$Ok>{fQ?M6KyY}uHLr$xPvOBBTia0PZ0H6F(+8_eh zso82#Q^-!nKbsg|V+256+n$@HllwCuU^&~$Ok4oEI;#}e>jKc#aZ_LkK#`rZ0^nDF zpAWR!y*?x`{-+L3X#k?)gUMNP<*_v`1HjFu;fF!Yz`P&Ex6|cnDt+O!my`GkQrMY|=f@`&L22(Xp} z5tTqPV~q}SN|l3+6=}xPHvse7`tE{ChWhbp0{nv!Pyk0$2 z2-GNz?Nh8<(lstUYALa$X4u^3);GAhY&zhUU(~HL7$SMj24q#^3uOU|s)jW>e6xh{S0nmHy zcEY(j4FGaIJyk@ocjXg(N(lpdTo3`#sQECB+wTh`-rUd`@rDN{n@Td^AMhn(2^4JR zbOn|z=M02^{a2z^Gw>-WGYugGG{W;2F8CgrHAiZy$Ce8~S7!)7kJlxjtK*3i@1hTm zCNyPeNGZ;xg-1jpWQQ7@+-eRfMyzC3Q&Ie2(F$ihKDo7G8CV39D_3evTF5Y--6?9x zjG|p*UK;X;GebqQk!QG|Y<2MQ~1hJdb)D=%opgA0YRsnOB#f&(zJ zM+m8@Ms>=ZdhG5AMqZ6%7%24kU}0=@YOZ`ov~40HzqvoPwRPw8Mx2L4FI6>-a5bK4 zY;7&Q`OZefb2gGj4em0S^C%mDSG86_Uk?6Bxe=hNlg)$h=-QLZmAIrDP)APNoblj9 zwz7ss=54EC8XSh=f4~r9Xw0q&PVUjPJq{PZhg!a}{Nx0s$t|6G0ABpN)XrM%5ugh| zSEuGEK~<&g=nwWVJ*`p$UI>7s_EuzQ>vz{wyM73X1ElifT{iO2B98$MTRQImbmaoj zRR^j^s;$Tr|Gizd^+?#+FVTJ)y%81L+8j4&=iGL=0CYpZvjN~aHAHoF8sZ4;z>*$^ zWZplOHF6@k153a*0v#StY=IAQfclT>Jx>61?IE7hV=YV`0He>o&3;z3mx;vxs& z)mPvD?epEM+rGH^>dr{v4=;mZ-E9aBrZLaNL^K)^TnH8=6dpZRg{~Hlas~!cr=rA- z>X}7RLR|=FNZEV20W4bz4onDLsT$DQ_FbD0i+%i_WBZ#65s#}D09kEe-;Bllv!$i-vLM!>T@R)nGvV~Ct&ENg zN1bp_4K7xjnpL+}NB6SahMKPD)4QvQn@Wk*u{7dE87(q4vA14|X|d)~bug$4J0aHN zh4sWuIMN(XMQcJ15k~ce{iYSutW_L2!%h$Zdbakdn zIv^LaPxI`9bGcIKrh2oQ&V!Z2Kh7p@crzD!D2W{pDe9H306;Gbr5-66^8MGf>hGC{^SZL(zq6XmepN2f$tINK= z?Z!(wLWcm@?NcDp?eSG~p}w#$tW~o!A$~+21pZB~Cp7;6WSEV32>gTM4_;e`c_;dn zwKNjM*$~e*%$T3_khQfLVRS*p*HF;{`%~d)9Gluy-FiwS1k3SPOAo;dRUtCv8pwh% z8yu>PCt|QZy#PCeq&X6mLxOyHJfZdV!~7}}sGE~B7<=YIO|4tx8rYBa*wP~Ef9Rao zHm4;2!kKXFCTu09^94vr&jf!BoKqhGY3ZWzy`15@lB^Vr*fWpCPXIvhuR8#E^^E`^ zS9y2J1>jjp#HSN61)VL^@ADNKRBPw2Yv?eZ+D#aWZ{7z2#>~kYV`Rxb_;ei|&}Re& z3~O{#r5KRw=xea}L~o-UE~O(|i*P|M$KaoT;DUd4uA;jHgn*FLwKu`$5ALNtO9}9qg-V1zHX9Mu6 zf2d=~4}Uzq_%T|KBX)bI0`OoRH1EcdeI#2`v4;%UERnRW>qAT&ab&QO(|klVzaPD& z8pmio8mCv$RTZBfA~z?5fP%-%3ByO$?E%`je?yQ`xKMV?%24GC5YVSJH)pzX#Prm~ z^nzsFKmZt1@c&AXraRX5asZ+YCjU*>%A19|TJqzgG*1oG-`rNYP*D>pk@Q`F?~C7; zR7&g1(l~2-*Gh)v>39UiY93|Nd-cKf7yw9Ez2MVw(}FQF(=x4$pWLVeVZXLoI356* zqizDAr&}cf*co^A@G_Vd0K0N(Go+5>06+u_84Lw!xy?&2=YMS;!t+%v5GK+Y0l&}M zy%jYWIXa+~B-c9GF>jh%-~_;B*#SvFWnwxm{XA1WM}(CCK5T4fw@9kN|gpgy;)3r|FBkD>$O zbATFioQ6d;qY^_knP6!?PNyO88|IpqQIS zrodu&X9ZQc=O2=+)8?oq1+QUbDRY6sI?BOHzWS$ z^veoB*EMwJ0uU1V9qT1T)bsNLD2-m98dSLuTwh!w0QS=`o>GGOsfP*j$v0@I5Xu-B zZO}R6G%k_syKuoDKzfVezC0t7)O>kG!%*w|JmSY#ZbWBv|FdJ7A_GVePcz9-n(xRV6{St#FF zH3mrN!^TPjlgPp@RG~-!`uvJ{ui#!DK?h9C4+M-b+QP|~7H3=lN{Qut6!9f6g)^F2 z$jezIayeCeG_?^H6aptl#o%1Y=t5&+f3(G|^? z3T(lOxf{#yTEAOi5~qrx$D7eyF(DMTMl5<_3eDF`{aG z#4OPqF!AqtmCMJc4)5n^8KEx;L;WW?|8I`amtEH3%V1mpo&f-%ZU&&P4?kvV$-ay- zWaY}VAL=LqI;(Tht*$Gs7xK(LE}nk6{*ZyOrIn?)^= zSQ<}9IBNkkRb48Utd(mY-3aoQL12Ef+AM0N+&XrG;F|Jao*%&_Ei1Ram$j#R1!sJs zFx5nBaca84|0@8mask*T7l7R$E_C(D!mg=V&E{w!J0oal=g1^DRbFZqE$yYT%{cSg z4kppy9pg*rszns9pe^;>UfhwYe%Q2%&8717s;Y{Jh(v^*og8awNXpjB3pLh_Ge&14 z4p01hr^g?=dm%2k+#MqK?Z#6hJLR1R7bZYITNFm8M#_7I^m63D77(@vYpKF`xp2pt z&PB@(i_K&=8|CR62IDHX9kn2?FFe@5_Tmj3Nhivc>?~5*ykVy?xgF%k0U%@EE~^RR z0?-BEUn+v+XkoK3U6@Qo854ioRcrad$-+)yGm9SNg@}qGl1)#S%e3uR?%4OpMvneF zJw38G4E#ya*+KNmot??lTEt8CB(5OGIU+IXaMK2oYWG+N7h}+Kb;x<bQrxw1YIp6_;J@kcatBW3qTiu ze=Wgup=a-H~{Ey>TeL(BYP^ht?BSv+agd{v zU=*k$@nLv$LOy^t31Q#2kP7kRih z0;FChIoIARaWbC&k=>p`mi7i*@2|$#`N7plUAfZy9{~t4*7lEel3qbKzDIs&KlHCI z(c3bWZlTaVT^{Q|(~;m%`(I5|rALX7{EwI95Mur&U?J=MP6u}<6!9EvUh*|>6=efuaxKCNbh_A zdb%ZKQs6b>NU04AJ7tL*~RxhOUJ=Rrs!A|2-t)EVGv zv)J~wCl0-yi$^^KC_7taf5jLc!oB_n020GU_#chSO)mBCHD%iHD3KeRr^oiw(2ilkI(^l$JEA0yv7pW`t59s%I#tz;r zbh}kcW0Nl-B!{slneqAf_dA#GkFS1wSJ=_-zWd~ttFGJ?hj4L;f1(WEH>@^EQLJ#{ z#>aP0JtFTdFWy_YM1!t$kGu#9%fA6yGQRhh_tvi7C^Z1GVg#U#aK@QXzF&|S03?yk zdO=|cy5~q|xwMg?!dzW?L5TB3HF+e)aaaKW#i|)*@wc%15~M|&t-MqqNd1N;F@}%C zVk=($**W)>BNNjBUHbw63WUL^8$ikfkj7=Tw$#M2{x;3$MeobeuSp85OiT>>Xe4&M ze3CCP)*p@?Y4ck5Klf-~%|v**M)&?}CzHZiV5kHE$N<3k>mNNmdUAgE-FHDinm}SZ zxrA4&H9!G@ZSD|D^6$0R`+gwD|68F+D5nkVvBT)qedl~0@?-KxlNK&x_u&5V8 zLzqC+$NFQjF%A7lTJLCDV0Z!@P*Jo(vIU02u@okOiB5Up08SwF7DNYU{AwvK(itZbfdOo=V!+UJJJo)dOo(}J~Xxr4Gy+8 z*EBuLjeLQLxz6Olur5it4k+1J>csgLkEi4P<$(7KZtU>EcW`9O`D1z+vyuzO0@Izd z=)bd_69Hc?fZ;qaaeirhaQxidu+LU10{@tAxh3v*JJrl_EM=B*QF(oH%cvTvP9$Ko zT`w+yy}Ye$c(~0{0DtorFg$xaV`ehPFP!%A90Iiote8$z!+)VviQ@t@YR{?YgfO`O96mo#STDh5V&yM@5Ba;;I)XxL|Eqa z)i)fjRb^*Ljb;aQP4p|S4|g6u)D1Th)02jIKF{q>XzM&2OoY_hbbnWlfRfhV>N!N4 zhT|S|z<(KjhO@PPa!$?6G}pT*Ss*{bWo$W-Wgu@ z=>-66nwaS(Q~HoUb9^b{lR$i2>VXeTRjx`TLjGXOfX2jiK6hdDTYJE75y zl9YkLZq*^our4;}v}sNlAH+@BSt-j#X0*3a_$w&Nbhg$ooU~=}(9E2uOU0QInaP}m z7lUj#vFDi1Lqv{@XXFt4(cqKc64CgJ0a*HDfEED1uTa){eg5g&H|{?@h2L%d-p9A! zJ$d)t8@K;)62pBr-o5+R`}gnOxqj-k*Vc~S|BC?NjgODs+5Ot?J5TSguD<>Fh#jas^gkr;;EX$ekNnxidHDP-zm_LhbU1%k3IIBdLoH#E7rO%4ZlkXcP8e`S zHG68rfCEb{bz^UFHFhnA5+ms`j#0{29e`z=FTVYB{@%?8Pv=iP{^aV(wR<=3UtfFj z>4T@ccOQN5>65jO9^5~<`_4zVKDx7Z>(l!H;HNi_uHAj|$>Wo2Pj7zv!@vIZ>Y1x2 z@qn98=TAOH>pTGH66-s+0QmnDV6_xC1^J8Nm9a8F6jd; zK^1;<)UEuUxf+cKrfJO=&4jz63U*AHq^KyunD1cFBtP~&u(S(#mVrtkwh@4mHkL%M zv;bI@$TqqZ0^otH>X0p|wb>LG%Qs>jL1$G}RdqF;pw)_?*TS* zFd}g(e@W#7;QE55>X11YRybJS1XO~5BsnqKtO$fD&7%oI9aMQXnA91@FMPuNo)ii> zjUbgd;9%-fP_?XlaHmEaG){HbV}pL&V>n?zeourmoWAMwrEWtGC92&>!OaWDaGqBf zfO`c1ciPz-Zy%k%cmK?(J69im`{5@iPu+U(cpenIyS8@s{>`Izh#m(W3#-ii%*q)(*hRH_X0h;g$?yl3f7c_Nv4XLO{-4%A^O8o@~jSK@LWF`e{!$ z0T9P+uQsO+6g>d9L85^9X#v3PqOL~*X(*Sti>eQVLWU>{R}CuGb3_Wv6}jZEfEHuKpP!$1?JQ|I7OFt*qnCTuh~Q!e|P@qgR8*Z4cFq$ zdtlior%s-^b@K0jzjO7>$?Nz30_weWd+)=0^B)0#CvScFc>d_ky{Kk+ckRi;hwnbV zckk#|=AYsTuHy-A+`oqikV`_v4#2Wv2B1ry8&zeJYE&W!m{b^MA$-Y^P{novAeDf7 zFUC0;q9y@A2MI^V$HOk_L1i^9W`atR_2JRS!(~($MT>Qq0)S|T&Hnps%vwxg|6By9 zL=LMCItO{*C87&mWsJGHnmq>PUlfv7slp^)V*1>rZU;qx1PY@gR_p*I{?R{70{|=g zM5PY$ByF#kB#v~oR!+!HQLwEV?*Td&D{PhhChBKycT}i@)3^X)=~+4o0OI`zZzIn5 zct4}Q(%)i&e~8raekEt%9nG2IiopuT-;Kdu5Y!($p9i3UbN4x9a)houC;+I>d5}Qk z)OglV=J0aUD-A$v5~Qx~hL{9Ft_uJn9j0Io6#V<&!N5Cz|I^w>55Il?E<(M%Z{I^U z3>I1dT$NS{fDhg}dFS3oM^CL;09@_bO8~sVBvR2wfMvxz3F1j%ZO3jDW3S`~I7MNQ60L=gF z45b1x2*P$z3Y<^}BO2OV81PIaaQ*Os%Q7Cbf-uhE>g?E#aGMffNi-?@gQ^ozT{Y5W zXZ%_t!%d+=El%M(izreJChHkzRFrB&HgG>HP+7D4;+!IGNEkNVM8697pp$Fc4zJ01ImI= zzIEdUs7C-ing`(3J15WJ&`(aSeRA{YJO&CM(C1H1;lHSLzC_jmLd9Gdoi#0Aiqp`z z(}8{@hMUMLxM9?ffw^6MXm@sZgf-g76X?7M03rpeM!0yfHO0k_U_BYccsWIIPCsG| zYGO6-1PuW|Hv*WEPMyCHzp)E(QTzvCjZ+5eT#T`<$l-1$JsMqARtE<(E`}977?`R+ znoct-Er>~r@oIlw93|X>vMhpNSq^3!i7X6_W&$}VJndx=tcGaC;Qy=)cu)d86iw&6 zn4Z@}U`LZ6bf>7!NHl?1 zlP+Ws> zV7XA~0I&%Aq@DF&JLIo~&R|Jlw2X zHm6JYBaW*E!4!qRpcApN^B4UXuG;0Umi^sJ2f7%e zs^HfSb*oN>?;N++t!mh*Q1V09^gp`d#L) zKl$bNnQ!&ud-L;a^Yho={r>BK-R?VQZrpx!Xa4cS)z$ka?+5@A$GG!DJWQo7BoYkXKk`x5k3bt-hb|*X&{fSNccy4 z>s``|N+wJC#I-{v1)vp9@_x6|b}m+bdOYqo2!OKM5z#2vV{EL~gIpUSrNe*dTC8L? zgK)0s4IQ{xPKOyBkLY4oAY)oh!{CUTFi`BgXMoJNg>bdQ9dXk`Z#2U6k5RnVI%-m- zVE>`R5zW5M@;3{oqbd{~89ko@s>J254^vGMJk3Rh4 z;`e{~=;_l(_dj{``yDo@yp&Slq68pnMU2PKLTYrXy`FPxbz0k;CyrSW)%4IpXi1_S z-C7C@p)+bCVMNn0Y7uUmk{(MAdWJs8HtSvaHM71W=RI+BEIhok(FJ_I0Dzq91GB+Q zcXy?m7D97$etfX8)8n-!hWq5`R3u>iM0Q{5peKW#9x^Xpiv&srfVi@*rpbklAx}q3 zYZ$bl?QT>>R3F$sH8eEUv;Tl@6B5=e7@)g&d?`@oE8k|>Qb<0jajqo_qx?(fCYm@< zM^DML{)TMJ;7m(v^Dtk3S^D}rdpZU^9Sh;WItaKH>zwsq&rUi__nvWaDk;3bXKHA$ zqjwtj-u7YuveE}2S0I-Q{he72uP^@b4}YxNK?Wf7e~hy-9Ka=x@9_N8hA*fN#LhS9 z&>gR1f78yoD}TJQSVsr)*OedtvF?pBmrKlSdA9)g|CYBtvM^p->*?4}HH~ska!j9G zcrW^1OSa*N#wF~v7X%C}pPTUvOV8w2wf8NDl(ddkF+Ay3^c&cJHRY^CL9ik zddFhR%M&XSw@l20q(CYbIW|o1DMG?|0tN};NI0c)(V9=T@e{@9=?;s9i1fGJWS zo8?f3?ZB_2yAuF8+sSZw#cLE_0|iJ|b}~t_?v$c0{wWAL(aPnGGANau-dcR%N2ohw)j|qo_ z6$)QUtDGETL?6yJn1qRI%QhZ*Y;KJD+s48!3S(rh1>tUqJ}Ghh$7jv|5Zh5}zVVsO zgpV$9zL~;EA1Bd~ueKeQFzm7ATxvHMQ83|FyY4EB@0mC-?@dM*oc`ML4LP$ToA95- z2NMH;TL9b)^7?#*$&T3MOA?!-3dd@}JYgLDyj?uG@7LR26 zj8he=BsRo7e~Aowp8fOCLGy-@qkMcQT=6Yg{ZXh8fC7OVpV4YA$4CB9jz$V!@HGDx z0Cz$q3qafjXd9`Z7g%4w3b0(X<63Ntjxm@6i8#;-%OPsYU32TeQd4w(i_# zS(bA_6|RqY%%EqXapU||VXrK=^rgsz!@pSKBzAkL19000&-+5&Sp}0e$O}}_C_!FC zt{edQ1^{}Wb2i-k4fMrReW6t+F9aY|q5#aln#ENu&nqA2SpXygZr~pQ5FgLIv(`Vt z_>Hs$z)d20AA5eg=sN2F%*FTk^a5y!X7M8V_u*%~95(y_&s!q!3RF|FlB#l16J(=s z(Vusp82*TSs{n$}*?1zf!Tg>+6`lx>B$R&P>)8%jZdfHf2AEU%1~=NgHDf_#l??zC z`pk=5w9^6WukF%`0E^QSzfkrKRC?acq>jM~nqI6g0l+Wua*)=?^h>5rh&`82a{eq~ z6|R2V=lx80%o!JBd+Vv~dKgY~A>6_vU3L2F!>Dbc|GgC}lG)5>Vs#r8<%9}`0PU0k zb4~MYw*-RE4=LW5(nUy&=&zP-(V}X2+(FP?jW#$LHTNzZC(OiK7pe9Ma3>P zbW{{KK=}W^IQNT>ux6trzA@hP+KO4+H@t#BHN&aYSgK9RbDm>j>h#=+eR$%;!RE+tK$7xhO0bk>x*bPOJag&y4zFtrfAPWq{@@MId`FI*KHVJCfk4cT zZgK#Y*L|n9F29p|H;q;)VHX0pF#&jhgDb`0?2ZykZ-6?`85&gQIb03wmnwz3gP8qFF;#aDGa*cxr7$D?3PZ;ujg= zUauO)p@k+US)R_xTBFh_qxpjdy-SlARR2EwxaDdGc{LyEts?+3rcX|(a>#%gMRaOF zw}J((pPq@Es{gD%6I_aT8Bq=VJ27J#WN;7~N)K}aEo0#9kg)hM%+X}O#zjFs-Y9km zC#n*`+Vd&Sg>TQ~C>(wuY}!#Xo=M*}J7;B0;cQf{!lvzg(Vkcr65vf90kX}*vn>E_ zW^vy{YpqG9z_RK&IL5`W5z}JXk+YRL zOJs&(D_Q}7%882x5F*PkdWbLRRsoO3&f2q;yU3#eo2lD%NsRk7EySJMh1?ZpI{|PT za~}(@Y)fH0zfWBX3UTj0S|JRA9NVjge&b>|H!G{TyszR-(zo^v zWe?Qt*z99~t|DGPzi9!;Sm~v}+SvQGD%?n*ith1AO++QGmGp>gSzJ}C*fq>-FHHyl z8Zt`GH|gSk*Tr z^8noA7-_F_Vg1ie?O<(n#U5&MV>fnZz~`l5z5TV_XPww#4eu=TQnSt&&Xa0|7Xl8H zt6&s871KC(A<<5E8x;2r#;J*G6Urn?%b}&(m9=!_$P6S~QL#=xApt-NA=m9wDS7BU=oU=Nfq%yM1 zr8RgGaz#~@19F-l;LS!D><@U_`M1 zSoh%-kowX_eSOIN!1ZRg`wwETZ=yM+=V8tquLYN%KXy+Gi>l{kJBel2z z$WI(MWC{S?eGW5e6@c^Yk2Y#yCgy0a>;@@eKe_u&(ji$i6CS+v9L2 z%%v3U5WGzXrc6}%>vPHo?c~~}nld=LyJAzOW4jx-4~NSmc>m%)7aX}WNR#Oa9L8C) zGXnsn1`mknc8D{Xo@QMWv(oSm+y~=YI8=1%On6qX`lKq8K^gdmBzUT3amO2PY+3;B zCIA+37u^8>zP2f}0f10xEQDTHxzu((=&Z0rK2;Iqz>JS2YgK1uAN4=C0R}bMUYf`x zp6on{fS_+?TqNLZtaStcl#3jIr;i&BQY8SmKt6rtdxa%(c*A}pVe}3X$)=5bzmw%Nv*s3P4aV0>Mq*CJus8O z&?BNvcz?r>Oi{?pq}_uf9YPgUB!OF#@OTC%*qIqmxv)c97*{VuD{v4(!0#Xj&R%(A zGf#rNw)^SZdl9dES=s;BV*l1bJ7BoFNiJ%s1JGU$^XH&U2^e~jY;?~6w?Ih0Owb@8 zt6!+~8wTnpfIwnsx1QO~4{ zOAJ3ZLxV>K602ws@O$s|>UDLScp1!Bci(w({|{eQKie$&!^fK?1O68PWVVt}xvgo^ zV^VD8Mr2^b-&xVH1esE!jBHQ=vcA5v!G#)bdr2YyN*L~=bs`O$?L_d zQ=8_Jz6gLNQl$c-3OeXP=zb`Rqz*Rg62~72;esDYFrpPBGdS7x(r|C59C)}j=%=Xy zbV_oW7AY`KAqTFgE@A-EZ?^mk^DIJZ)j$p3P9G!M>{JG(qKO2eaPV)T#wAulyOd;5 zRv|gza0&q85|rt^L#kn5tdB?;5Jn?h(P5u$$b`@Y7SS+9rgb}Ol0$Me0EjwEQy5j` zRC@$ha5Ub1mI}he$@!acNQw1~gdA>4!J}CeAeF#m$g3zZPX?7YS>6<*tZTBvPt_1A zs=oKD_AUUhZu0_gOIrZ^x5c}D+(DTYR!CsXyGD~l{SW=brMTaTGD&1#-{^XY5ui;t z@0%ei)q^@e3$zmj;?nYrns62~03}U1(wV-zu#nDPzT7jr>=WDW8Pmh*p#(HCv;P3+ zOax#`LpGbeoKB}N|KxHyCVaNx#doE*Cu;iDsHdf&(FXtuR@OI+2T@eS9PNqdL^}AG zu%=Y+kg2L6btpZlYnD$w_f2=s1T%hB^`yfJod|L%v9xEO8jsg{k|}MyoV~=AuDM^F z;4JS1z{)_to}qovdzXLQRrmVqn|chexV4Ec=qms~=8`6}o}jA6gM$r?3gBm%=PxgL z%EZmNTasCbzav4V`DxY3bfhsRXtqw;r4~gmy@ScUQN_#!5`*o3eN$l``i1yf>pw_ z&gKgreE*&F{(&*C*YbxE6<7xLXVVM4kv1+|e(=E*opvO0ev2TrL_!3h)cr}^p^JzPWU+SkHnaiiX^kG z$%e3(L$;To^_eBkm6}Kru~I`!Z_0ZBQfl11khK-DB;ZOCt6cDj3D%L&PG%GV4eb;Z z7P#Vbacs`4Q<8j2lU8372w1GjSK$C~xNgVa|SWgRfuS zS5x9ZDEHxUIVeKn3AxK9h-!akbR`g5XcAp6kj5D`V#_xOD9jnrQc&nWf3F!1SU+{n z7>$fj>s|-{NOfB&@T;I#1AzEtM8AVTo5F5n6IQ4%Nd=x9IIT#$L?2%V24h8i*~{}_ z+wePEEL)GhtQPAF>oMz%6gh%jcAMDnmJcBF#}vBI;czf)qhXVs9+jsniEcYRioSf2 zZ5cg~juC#SF1WyK0r1sPsjmZE2Ou&eF(@QN-%3_3B2hu&WvL(nB;)r&TWAY^o5`~1 zKaRC&2QPhw1bK4sVqhCDhz&`F?y+RjLqeR+Z~$k8BL{ZI3qv*%y>sPj^gT1r$JxAA znD<#*W3*ecS_OJ3=FGEK0I&>lDY>dho7oIuOaCK3z!C-EdKHO$b&z~XD`Zi*&hfIz zBMSY9C}bUao6NKAON$UB#{0OWakkER%10rN0IM(=g*f+tcI#MU3@wZ%BPhGY)W$j) z2~Hs31_5BwM*4ClDLB7)7(UkP)RDbWVeC(Df&^+`fomF4U<&^*0gV1oM+>2$EdZ9b zo*Mz$OAUZu;Q0ODdpZ#TNsRwXqH>=1g#SE3iutnVjAo@kU}e~+a81m3oH@YE1K84{>I?-wY8<-8_n_n6S7khTqsWHZA&%Q zv}qh@%-;jA0?{@1`3jH5pzv^wj{gB`37jNXmwx3+y+ZKIWx#JNe(lVu*S1u$dL97n zcKRsdp7?)Jc{%YE6@-thpab$80QvI~C}w3~9^WP@{IjzNMZ5tAqYRAkG0d$mEp{;CxTd?D^)Dm~f_3@gpaeBLmINlkKM)=8pLC zkMOL`01(~3zX5$ZD|&z7pqRI~Yl1K-e7V-|k9V7%^GEa?0405*)dN#!HFy}N_Kfhy zc~hXFC1|R$5gqJINsw5aT+=(H8h*byGT4xk@>px^of%2MWO`((S>MUa*8!+4pF4M~ zfPXIKKzc}Z8vfuY{G>H&_E?!ae0Ve(jH}UT?eX)PsP-*!y?tpEKhVB?L&<3`t&Ar_ z)vkf;&_3)!esAhWR|Nm+Fa)^x@YWUpOIXhZ;H#4Y{})9r)8ST6FrG2}!R&xe4tLKj z21z}zesXqbpA6q)PjXr-gyhN(ZAC1mggfGA$-B=T8f}I9s8|D|$7bWOnsQe=%#qd< zV**zC+~JJjhWnqaUQE_B<@q-hfr-+d_Rp~>-4iMn6o3luobU3Oqt_n4Z~>|F#EpVvF2V z$rgWK$0VY^9dCetzq+{vz*5$RS>P=I{^#O>&P#_fs_ge;e{nFKqN-h#(Xx#I6prp8 z0$_E8!(Y3<{uuyWu_6W_kTl@w?sG%<#hfTV&_u7LrijOs$sY#9=?=Dpn>a;pr)&KV zw-79Pmd0EZLBkO&gfdBp3xTnClA=B4LI5cD^&u{#$S|73#S)7-hnEK?vuZrsema8P zAYM!#@&54)&eC_*=&O|dFuG!5BY3arAjweteTiZ6SA)Tf-?>W|GFHm*juicrtm`*$ zg#H!`p?6ihS+Q^7%8t6aH}g;O^rM8iRZXgMM;| zhAJx$c?Oyo-U9-yaRb!|C=7N~0)S$Z;urwv29^-+5dc%Tb^?H8lUQkl0Km)=8q2W; z+IRZ!P*ip&68nY-fH^lEGt}1?f^&4WY-P4N09NDyxS&xR(-&X|E&zz1)AS5TXytk` zBVz~~wS8@pee!h0TRu2Fg zWYLMP4ggMC08{|Lpue~QIO)l_K{Q8SpHqz=j%XBG>4*TS02ImJJs1%HtnaN=W%!v^ z;c#a+Qeg28fLd?Fr3xDHQ?nukW{qha2`@8BfkW|k#`rb@pm$|?p#3t;uzoyntc?o* z@`(#U)g5vhPTT~4?P&o(R@QDh@&F71fba)c4}cu8;voaMvKs^p znH^z8tf6*!kDYG+QPftn;!@Xi7Cwg#zzPvy@SPO5+i7Na@T}Xht1?6YjQ6Az$!qgz zKqZ1;nh0_jwFmVwGPBYfP^SQ(qZrU%+f3z($~mh2s^sSlRU5Sp68FO9;2YcQnoIf3 z=xDP-6D_$Jn0Jnw1|`eznMIiwz7c?lU{d13l6#;-wQ929`j4bj#8g%`*@3C*Tv4R& z{fL$VF7Dm?N16ly0Dt@1mP%4DOo6x70e(pUmU~xPN74Ne0b(77nvH4bna=~Dq@jGz zA=43}F1_6drmt9IfNUG%Wd{!1idX++?_7G@_{uPD&Z#4xOlC5kiAS#6v1KcE>;~V0 zt=NAjc5KUub0tZ0zo}G86cDAP1(k|c6ja)-kdR=(A0$A61t26o04p}^Sg~fop6|f( zp0S;2XjQHfb^O%19nU2>?)mvWx91hQQHOi8pQOy3W()@0FqS^dQV8e6+X51x7!#?m$l07n#M9e(_J zh#ECDWOL~wo&}~&8>JHqX?$VIj1`Va1^{x-TFFB=-9M!@KaF}?cPuFE;+X|cSegZD z?4(sy4%n4*(^<`rrOfk>9>#UBjc{co7NTK_?6mbCZgb`lIGe;;iD%zG=RI=;E+Uz& zycUSX0+c_P6BmV0D_95}Jb*ALS&%p9LQS4gTB^}N7&kXMR#;}~Fhv9$SHAZ;ZVpY} z{W1Xfz#xNnwUORbdrLxw_G;Mnm z9yaXk`e;~|m3sMnN#~;DFlgkOE8pGNuy&iH2~m$Q9P2Ut*owo?|aosiE~@R?w!45FWan4|9o|I^|Xe)aE#fPexe`o zRydfmkV^4-VrRLi^3Gqxpvd`y$CXMhqMCwWB|Q-Bx$VdIo;=y^Z3UKY2*%z{B9U0a z1c>N$tVLugfepQTonEcH7dbvNu8X(qwYXl2C+e9*N#5LnA_!atgH=`4bz3+u30HBS z<(+yWH-}3IFT@~IFXoo($8TDbBy0q^dzb961_111y&HgD?*M@Rs)joauwGwItc6@| z>sRF%*NIZDUQcYV>70s)2%SrKK3)mAmzfLOChQRATzCoz5}BHMtZrF4Y4dzW=Js8Fy)H*0s+k z`#^eJb;{z-`SO;IJ)qkX4aI~(CR@SKXM2WN0|5571_1moOSahIuF>%O-C1dNfN?su zmp2GM^j9W=p2S<0i7pBfc-}gYlgK37T`nOgpMc(9OU`M-xahmgjKte#dwXI;L+OGE z-9|bj2%f0jq)2}m7^3#G-U+~uJT=9CTVDCW`9HdUUX?78eZMQ2;5fwGjnavVx@TP; z|1mp4e*l1tT?V*bM*BJB*Q=roa;Ol%>`JxDRB!G|mE!-vKr+;a+^>|o(jB?7i!eq% zF^NAm)D`TY2LQYV8UXOaBI{ws$m7%<#SpcI-)>a0Bg@&_4Xu}ok+G|>BGt0j-7@7x z`%;VxPGauDvdT@9Gk1jvGLojriqIthLjvVASl3zeEW4}nV0l6JB{^=yh03YZ>`F8s z15@Oo$fzz>TtC)KSGm)Xy#!$dzbH_?c6s;aVo?0uPZ z4emaY&h+A{%-bvc*d3wYFQ!MfFD?>Gb2_^mBw(Sn?Zk3!&EOv4r4nF>iwFu64_+8L z7rl@Wg*g4e3s*0efpJS07Z=+vM43B960>vX%b44Z2(`;Ulru5+0yf-vcmnR0K_s3G}V%Dd6#k?Ij05~(YuAT#Xv3P5|xYd zIo_aTKDhAopLiruwNjR~)r{LjI#~ny z=9B4_Rm)m9p_y=5ESP$nsjg<%Pk(rFE_e@sd(EStuWtOjnkjOQ1IZetm>Bh!strAf?t;)7T@#d_xvA$BV6?Hr-z)3w$%EcP1+xOvU_~wqDIeI3uue^|+c<$V@Ey-5OXa6*6+u8#4+VVZy0G z5eP$-3fdl$!Ukb~AZRs{CZpqoYSg!)uo?5mf-CJ!?l?sYH9iZIm^+K3aEUe?0x~rd z)jEnkYO7qn2Mx6{jcnk~oe?E<_J&f!-5LO}|1|*Mhs5>e*;H1G#lk`BEU7z-DeSq& zFR}C9uN(xOO&<>G&;Wz5Ii0_6Ws=+(6D(tUNK_ji@r@ts*3&pr3j4sKaE=Z<%{E}3 z&ls16(r4Dp(}1U+Bp1IJ?JQaH!OK94ZpB-s5Vqi(hZ~w7Hq8Ox>L{+$sGT4~>9Awe z7YHuw$r#YqbKj($a3k0uJpbKhiNZC;%R7x3T$7w*rc!M{RZgQPUgiU16EH&?yMz)T}36v|*!HmuF};RxL%R{ z{lr9YKThMbx|)9WNb@h=gwHBABkm?u!k^hHM8o7Sb7#~)6+J3R>p-ElHD5;)!NV5w zF!G^jXg-90U;5I>_`&L@lYD0x34wsYT9P6VUXE|g1Qcw>hjrf5XaRAd5M^~| zSAlVIp8*j3GcwuO$OyWY2;u_sF#qC${8+NSo9NdK8(U(5Ck2C(fy->!C_rx#4SG#rFfoCb;pfY$%1v$_ zqi}2t_lMJd@RSPO5g?=;O5=V$`QvB+Cy$Sl8`i<=*5m+y*Fo=2jP4x(uonR?J)HNW zXEcf+1hGofpaG+De0E0j!}bnkW8rAVCcSW#kQAn5o_WxZ*b zoZMKq@_sV;n+i;fOs!T7>I9o{D;%4c002kFl!J}msZO-ri2#XJ0)S5ZjyIroi)KK~R(on3nEn>R^>2M^{B`=)0D#v)0|0)g3w-_(5g_FE zqYoCHPO6NR%ZdG&*aYGa%KsC1)HGV0e1`Y65r`6?#gx@TnbF4JCn^63^`D`UdFw*( z4ge>7juhlc(^1Gbt3gds`fLpF20lW_Yl;4+a&Ryn3j}AE05FCicti713{7>lUW=$) z&}_+ifQAaG2DMZzZ3+PJMJQaSL8j613wv2qCecDWzsB^Ih_y4@COIaRF4@ggb|r_5O5;q z=_AQ^kkENGuVGYRd`j^n-+5Y^udwaeyn>*b_@`}DK?VTqZ%F_m0PKkk0Qi=ec>Ig| z+8vBFVSFyXl~cKZ=6>{xDBOc7siXGj*|{w|(2E~!XbOqUi>q?Q45a{u&9ICBvA(Pj zQ3Xfg((HW=BVA*lWf_FDF6^T;l>YM z%Y}f_6L0{)zSaPM9}r`kKl*O)0PTgsx)Ve742Q4~dgi4e9Zz8;RKsppy89+!xLUFK#^EpP{TgqX>zRJj9eYD(|e!p#6IKPRyt` zV1G{oa|>}BQQ=nsMxMO49}U7ff88n+IfKEev{O7>4F&>JQ`wc~nozlvN{;%d*MMf- z+hiQqiX0a-5j&^PWmaEpKQR6 zA2#{f)$D1;q(aVbV;(*`idcXAdxvi(2LQYtx&lDPs0%!p1$K$+#qaO0hhvJ8-OZF# zA@_3Fp{3`iXukn%y^*kO6hT6bKSQewW`kB0%bPn`G`4FS>zabotML~2N5Yc9qm&j1 z0I7u~gL4Of=|m%|`8<6m#*JL2)@UqDXV$n^0&o~)vJ=gt+1aDQ4)sgDj$fb}**RR; z-K{p4jn0~7oUTTe3ys~~##v&{_OOmI4BxC*8@OgWF3N%msT->&E6-;4QFv^0H(A@Z z`KCU-yE6OmUQy*Cj}$8=>ZB)667@uhb?hpJ)MSP{@n8QKYhZ>30K66&0Pw>K&0Rd( zYV5vl=hCVmV`FDA^W)k=qftcz?D<-u0VWDZjic#$Nmb1rK-Vg@>hA9AdQ#;b01Afv zt<{y;-RXK!mVpWs7-A>vuBZRRxoVV($z)mxMgVlIu7n+W0qcReGD9JiO5E{uvY0Fx zMkjX?mjLCFV)D2+Cp;aeZJAx!NGFrUV*1i!X4vadjm;OuQqoX`rw2e*=Eh!psfbg> zaF5BlFjZB)JSqo`@geOHjN{{U%S3|!0EY$u?0*da_|M3mTU$b_q)~Y(2W*j2GMO~W zZ-hK}OAytI9dw5Y6dWf5jIEgu*aUGXYpclJzE}a(Lwmyt*iJLf< z_y{4{ZF6#Rh~XbJ0N^!{1R!RAFA+EZ;JaO(1t&Pz5#MKFA`ueV0$=dEdk1IG-y(*M-a)d~E zAp+o~K05$l-|KAv;_F~1+&eYgF@U}y(RPU{7XalqZtTswZ&~4g+j<+yZ4+fBAYrc>6Q?YNCdUredBRn zJl&3j%8d1b$xbq`UMz5X0KjXYcLLCJeJ$}h;~_N4w+m4gq83EOu8c=0wL^k=<oC(Z9S>w75h4hxjv#Z21}H=#*Yr3etZI{;u` zivV~R0PLs(z-!tA<^a%ArHQ7RSTbnp5+d&qQGC*zt{pBX9bU;LD?GB4nVzmT6Gfd1 zr)-!$bD6XUkIO7SbK8|>p^(`&M0Ze%_H89j3WdVsoS{06k{p?gV&dMJr_Y3Hgw^X7 zEj(Ub$P{g-2AFBYr@yBKG%eexlynM)>A#$@TwR?ySj}3s?FgrsTka2e(!g4BZQc52 z!Z20&HpJ}nN+av{Kb^Oxb7XPl3@H{1vl}z_!`a;?_8h978=Q->zE-eswia9|EZLnk zM~vsDSAxOd)aq^}0(Js`{KbzRezRJwfwX6bhwWy3auSOp1_11Lbt6D2cFh4uSbDgj z#S}Evx?!3?laTiHN3e9C53gc*Gv|WfW~)gwK)9P|R*j6-Df*Kc-GrE1L$K%Vjo)*9 zt@(1}U`jin%?;d1+|7(!!Aj+|d}4F1*f3U<yra)OhU6D#Y1(Z1RV0sx|JN@rv>I_86O#FUjm2uM)e zJDTy`gvMYqYgK48fSc;lR`38tK7Swpn^~$>s+O%hb`p}IU_jf=sX!q1?bcVquqaz3 z+n8XrWD@>{jC-khI5&Rs$)Zn-f@|IzOz?Em(&UXkSq%T;`I_t#o24^tlwMKbH`x7+ zOmc+AUEl`@hnXLLc>us`p#cD0^M!gG{FA%(v2k~0Sv4uz2tD4Ps9_Xt8AS*pb_YKV#SVdbyR5Q#?^Z4}v8Q=N+ zzJu&a9i*jDCEmzuclv8DFl7`_)tnVXtOuhh5OC(urMqW;0n0`bP+W$~gF07o1X$?#<3W1ro<^ z`Vw0`Y58D{<4<4)cPakkH!@_U$lw1eN78*OW&$*m_G3zP3&%q59W(Yrd z^X16_0I!1v0PJa4%(da%Lh>Vl&-$SO*l}~N77fGS#qURRjUiUiX#{JtHlx7$6rC`H zfcpu90Eh({B2-RJM`3!biq2BN77UFQieKQ}1dpbij$p&o>#I{rfB-lOlg`>6r4I?& z_fq))iah-#0I*^yHzfdprkSl=3(+o5pQXShiV8>k+Wb~r2mgSzJOGILA03NL1sBK; zMsFn>!Pp2y1cP&6$>f}Yb*R-li;D;>M*`Vq#3RTNdSxC^1=AD^Sm2eSxK6EGn#^Y~ zHnw;tY(2I;bZ9026!;qr`0+agSL4of{ZRV*;4cwyeD!#e_%{Gx|7!riO9{YBkqV$E zgq{^JCd~8MAenDcTR{ZUG~mc~@$ETt%w`pLCqxpqL1APZTt{R6ahe3F^@aJc9DvkCb zt~&s$DAq<6%#|GNb(?&%plLn@8bwv-U+(Fsz}DVMn96(-Z22RE;GEeHw(cu30`x8V z0_!l}#%rSPfOYgfqm09N@YNF=|A$Sro}C&Qr~4o@)KZkEfbuLwVbbVFS6jjBY{ebp ztRZ9?(G)1V(d0wW0D#v(Zv)VgmtD_gFbt;5J_XHZiiYgNjbjr*9FM<@YJNq-2Pn8+ zFvzFf+_8d4-~mG`2sVG@z7Dd5P99PIiixS2M=y_uWLTkRQe=qgr*u2M{3|p-T5*AS zNo22(epng+$mqzmT2%3)@p}45Sf(s-qbVQ|oJP^%oXwe31(EN=6q|B#kk8)?QBl_? z4fy$y(YA8Ohko(Hq7dL`X*THhNdV&PXzhiHLbm-90Hnb_nLn->A?MWAjuim^{PI{0 z0Avz?tFf^WveU<*@X6OHPn}VT2FT4D_lI}%etd}PA8OStxI!OfYssMj0I!KI0Z8P# z<^U{yGmk(I=IP*-4>>7J6pFng8W*Gtj{X2efSiWwF|wo}sSmjl`(^qMx#%xaIbA(K z^E(-Ag&?d+zuG^Y>qF%cPpg=P3;UR6Kno z%omZ*;Z~TcfC4~&@Th1CnH9i7{*OhX+DITj9Wia8tF1PwAO_l@Zo-(xkTVvW&(~wb zDwy4CGc=0IQLS^&q69#M;BpQ{P7F`R<$e5q`J!yexQ?qdwE+NS!>0KE*ua8CnBG(iAVQfG$AX}G@m zC`he^02qdOJ8{iS);2V<$fwTP{nHEEBPas2U*wK|ysKqDjUOjFdl@Kzq;`g8J$?}V z;gSTP{lp@Zc*w8i@7FfDoPn;dXt995zh;5ue&6I-ie{EDtPsds6&liA2|%)X z4(=z)+~%sO9#^vp6pP7RQO+igP~zuPWPa}>D1LK~3qk9cFJ{3tj6q;r76el{IaM-< zx4u09U_WaBz^(wM^7ev;loW7U|HKzwIBuC-B5Z#qfUp7|TE4m`*S8?o5vGP?ermp& zb+(B@L9@WZR9l5drwPWWM9?bUWMKonY6>9_G>Tu)|GVuePN%}*(Z$hGtOT^4s2o0b zqFkKLYMRnl0F=7`g}zs%F;B&hx;jjoL>ghjepw69*eW4tj+0Cjt^}v1rec9`bU$O` zxM3l9y7F_)H-Tk<;rshJo$AW#Wp<_!#+so|u#Tk>r-@<4)mCy^<|Kg6EPS@h0wdX= zYVvhf8KqpC;f?1@9g{)$$)=+Qy z1G=_mSme_lc`$vtu4$T8O&Hw`_S{a?tZXW4?VrUv?oHSV61jNxDxb$zj^|4uIv4nZ zQ2yZL@wx5kiIt0)BP+GBj(uPy({u`N>q~{%SJ7zHYP3s?bsSBMz2?hQ%F};hYAJ`^ zfbIG@s%jVjyn%?9v1B51c9=;N?M`(`8`<>s<9kio%&YUB7lK`^w+m+{59V}=)16Jj z+`y*A!)7KI(Yr2-0@2|THWQwqko7z#Odg3pxmP$@F50~3Lc>+#{BiR+HZls{v!3x1 z0U!Lx9+Cj;GH~)wKl#cv(9eGMhd=z`XIBgV0DT9g?lR}dD}^X~f8qbLe)E$zaKM!s zI04c`?q;rXc6RUL1vS;4&2wodLz{W+t`QR;D8!44!uL@)hZ4wbUK|Q}-L|Qr;>Ag` zy^~Z`+18{h0#7eJ{?YTolRZN?UV%6g#?#xGwx=J(xNV0F9rywODO{E{V+$dz?{7%_ zIA0J<+q&D%g#t6ig&~Aa3$%KEr(Ec|VUVFc*)7U?g-}Yi6{6=(Avh4H+vJYmAkiyd zLP3g1`t|<5GcvQa=NN8C>LT#7ckkZ)e820bKYdpj3hD9AC;#EcU%lfP6yxOoM}7Hj zSDzgk0Pw9LtB?)rmY%IJMRvBS8WDKPp@z5wV8_i09x<}15yIRiF#jN7qg8X9GThxE zhYNuNhEO@3Ei0WE$aLZ9L&?089HA}&kSf(-9mL-;+CnB2y=nyADBy!AM3b~4hDzW$ zoadN)TSJq#x=%Xap^QG^DEir{fLnJbZ{6)&0rZgP;B1tUg*Hm$HJ$V6Xy+tby*)HzW85-$Cd^>nGvc3#n*KtJ#8-fMn+(dVAG5q8JSVpXA1<9{=#2(|r0u|A+$m`zDE8 zJpoK0n?py`06r1mBmm{Jt_mUAIA5>aMix0$_kmyLs@{9XfBYKUcFFmf23f3Gwi)*1?;x}6JqL*@s#L}A0x^!Y7C(@E` z$Jge1l}f!@Q{Oy4KQ9?DeR0#)A*{QEnUUi54q4oC;b4=x;Cec_hnq*ZyZalmai6`2 ze%m1+W7ijexAAXss8ve7X(1MbBmjwtW-CdB#Jo#Ts&7iiDE#OGgCC7auj zuBy6lOs>!@iK zzjnU8y=DvCq!<6-R((c5<`FMWYSZw}rUx9CpPkG(z~jlsXN|^IWBT!$?V*qk5+Yfj zUa?MRYl#RzC55-=WTm#d(m482V$&mf?^LDAH)mI7r!zU*;}(x#3d@~oj*AD+w^G@x zwU97$iY0{rRE@-0ZELIX%@eH5>54_RpFiAR*`2OL%-hT>!@+9{K&VyPIefkQ`qLl3 z>2_7I-Nror^l)L}to`FOz}P$TICJ*;H?Ke4c>_p(Q~+dM4Z6U8XYbs8)5z{Pj?M`d z<6(wj21a8PKp?K(auo;&7;G$T$2ZxQ?>AYNY*{NsY3zq6a;%N2B-^KzQfVL3_N7wZ z`qDqK-`^RqiS13AwA*e8-)3oYa%M=*y}x5F&K~6fDYo_G{ncryf*V#!d(+bwVMSvC zJ-u;sx!!I!voSJE;O1waI)&OS@8SmbO?5m_fn;A zmzID-h^hcJ&!Zj*z3a!Env{1zi9QT#_IrkF@m8+rj}~_UwO=EmDY>dxZa@6_=QA24 zcg6JQC&wWiZH!v!L!B`u;d&#SZRb=K)U5NLpM)LVgrsZPElX8puWEu^i`bwS2MT?A z4%o?8%-tV;_+j@?rw1J>pq22>)ZhQ~!}s5Rzq_@U5(bD@gYn0y*+0d8`2L68pXRC~ z|5yGuG{8P_HrYB3seXUd?*}NUaI&G%C{@$90dE=i?y4(0hifpp>*;ju+0Q>epS7lGnNLS*Ev+)lkUq z7%0KpmFka0IhJ%2d!-R6WeDF)G=x<;3Kx@szi*Yf-Jq{Zxgyy2J~s+ayM-OVPyH^S zBv5uzM_@>^v`YSIB!nB{zQu_P(So84In!UBe|X}!9u{-H37>UQRoNeTPWCBqtcxn4 z5WJpHVI7Dv3FWoaoEde2u|MvbiBzfvyiZJxe~Si45pM_S31nE^AHaugrnlZi2DpBk=Unj|YkoaF7B1IOB2+782h+wPqY_Sp>rwANMvU=9SK%jHFJ1Hz}6ncc5{0j;c7;{xr-9`-`MwZ zZVrW{gTL>hl;n2j7QXxLH}TiL+XVEJk0mS*`lb)SU5isr*||v6_h{_VxO=zoG*zQg zq~A%;c)i4P!U%3+LlTe?2z_lYVom}6+?`xkszm4x zpUc6h06rjc1fnpG_T%MQf?Oei0BZURrx}F|Fx3uuWinOIxI3CY(y+yvfZR_Gvw!`Y zwc4D^UQia(Ac*?gJ2~0LVYvvNuh51jt!@^RNR&i8EuYn%X9yr!=IS^E_k$jL)y8Gn zpsB_?`br1}HF*O^*!j|0x_9!E&6&Tfm!T6QUhXWsX{2Y|C&Bpx72gBqsx=l;4? z-2MBX=Pq7p_JLRM=)9-~_Oad949=Y>&PLEv5x>Z}QpR5U)w5|!7D6w!F3&g@lq+13PU^KwmSd&2Keyb)EC@xN#evRiosS% zV^l)vCmJCC`2gjN`oW$HiyUq><>Euck4}V%shy3k0dA%}<369qmBh%3&{v8`D#ub! ztyt_45Ys!`3S|$50Uo9)9}i)s1r1N@8eoXPb&kOk-VJ7G{IPFlCqL4iE?B!tQ(Omh zAK{xP@|;V-PM-lH)4;xfS0Pzj=<#Eeh>u3ADg`)4p;x3?LB%n*db-AF7c^I<-IRKm;(O1o8HgYIN2KV?|2^x#4%+$i@*SB+w<=qu=tLOjwYuu z{z5wkK#o2VV`Yqham$sRCmP*S=`(+D7qmBQDpdl~ zh6;NJd7C=}|Ex}^vd>Na`8aCrY`_2|9;6{~k_^y|x7uQ!i6{TQ06^JemzSP2;lMeD zz+dWspLeT%E3kiVU;R<{#OG)ASqHo+?5}-u(Or+|zkp9I$r$PO4DjGP;^TDwyekUE zVRbR}#}tYf)8=T3T!v5)L*U_ou{NmPS_Z#34fN)pT z-^vp+ez{?8Qcr~#OEr%}@~(@X@C(YnLv9t&v^Dg#zcnvhnjj=73vGFf`Y#O7 zGh-r$x9EdTRx71dpFdLRFhN7X<&;#@8hGEBFEaw-haD~HUu zy^c&fms623|c>_MoQBMT!VB35i-X%U!+Cf&h+`)DZ}V)j6tugtoXd53yphbW)Et-8$)(d>&L1!abQSY zXcphVB8cBFQv|54&V}t&6n%EaHRc{f19URO*nE5bn)VEdW57;_p--QzcquJ#6j?d8 z@d{uR!JT5k9aFGaE-6&RHkg10PW}p3%;8nCmEH?;;fmS@k|=xWq0G#@J<<(ffcRh? z{pE+Z^BwvgA&JJ-emoUgaYt5CNd()Ng9y1Wl6!=dAW!p(JuRtc-{Mt=44z1oL>qHH zmebZJd+Z=-1e1W2q?x8#t=YI@LjyWlQ^f?J7o(xw=7Alzj$lb1UKzvaIJS$Z5X1Z5 zd{Yb^?;eRh6s%=R$Hx=%jP=gk*S1^dMT!>WsAW!PY`22~5{Anko@s~(B@yarO8hBp z?S(v{^ihWK>+>C(=DAr%uk6J?;lqsRM5B>DM@B#LMoP(^8u;5m)=GSevGtp%{XO$zRv;KuPFNqehZ>Mhob*^D;M&=P}SA_1&#aOY|AdrRSBtrjDu+V39}f(}__Fxxt15lHh9Tdl3=mx?Sc|{@Fjc!TK!>(n!IY6) zEauD-5#6s(u1On*@6QNZB8iI;mn7B;GcWKDq|lyA5vUzvoW^$3A6|wHsmExN;DC!u z=)SO2VBT>nu@U&x+06?0&mn~%+UTo7MNTPhv{O@ zZ2}hKQf5Ap$gk-NyFpK5FU)*?A^$4O1R4bUm{>v-Ee;feEK(J$5O-SYbOpZN0; zb&>Zj<9HUS`6uy|W-$BK0&2!4=Qp2kO!AS3y^8qI!t?Qke7R$cDAx|v?^FWI^9wU@ zz2CXjLihbu?2mu774Lg#4Wqx$NWALA314fB!-sJ|ms-jH>IIe+uom$h&dFuTdw+)o~CdY^27(_`T%wp68J=(^oC{(y4 zpwC@VW;lrVfwifvxFUR@k1K|&7^D$lt7m{@cJ{4^EoO6S#hQ~qYcbo1QAw0b9745z z^ggcULLl~h$^e~TX@I29R{V7gkUl?afTS)xmRgW2nE4~P5qI{n6>W3mbP2U%7iCT} zfB2t>q=hH;-}1H--D}X$=M7Nkvn|+ffTY`xZoq&e|49V`oKrvRGpoR;-~du^qM@`$ z{}Vn4iC>QP;oKpH4u`{8kmzXl#S@~F3eOX@@Piy;V2RKiy(r-5w;PB|iQh!g6kKys z7Z2g=K9L)&ptB#WNEcICKb`01WyE?02+_LBsLhm?#JAZFh%##YKAY)Y@d+}c^XqjR z&?VDO{4IxgNf>{ge80rr00Yk9HWk5`m93J|Kjtj)w@`Heu_ee-}uGCd&hI^ z9^>s9&X;@xWWQs&#PVLbc>Rn2d+^Bt<*)BI{onohfvpqPKGf%Ahh<%C@l6~^t76pS z9R1=#y}sJ-Y(o9f&hOFTp}tzEMzd3nTbHBG0BW=gmc4GS-$Q5t3YFo0{AS9P;lzDVDDai$$!K(^fQ`H6xR=GFbEGLnk?&kCpaH)4i95Wf5S>Bqx^fEeH@tu zfskeF#sE>G2vGM36*_OG3l$~U9~UwjQQ!RA@6BKcAazjEC?x$F1GLe>>{m6wUYN1V76Z&50<4{uqEBeL}#AM;ahlBZ+aysmZRE=Lx6u=QD|KqOrNrUk zvt^y%5Tai#pDj;LF27n{1CGCE5K>_N*-^c|P?dOpx{qHRC-8)x zEcbZ_bBMxcc{JRB&R2iCdLVYE#TpuSM~TQ0Y2 zPF&5`80(qcRMT9FoWI?#S9y17*Vvrc`e{pOcE3)53Ma$Rxjffi%BeZCvS2^d-gN$N z$N>M>9?4Tj>n5JiPV;%@W?3E$mrfVbTa`q3=zoJH#QdDkpKj%Hxux~14V_YgP`9k< ziI;CzO>6|5+t&C^1Q(3guU;M^B}FfVu;a46B-Otf?CR6ki>;leLvAl9wqCbtUIgvZ<#!Pf z!+=a5lhwrqX}GL9NOCgcps7YZ;{+Z-qKX4yY)pIdIucSnghrDKmvnMUFiP@mi;n&J zXBt0N4Q`_YH`D&8FAD#3%b|+Sg#mIsYZg3XI4bT3K7%n~2jS^eYd0oC#T?$GWPp+p zZmKfQ96JV3lWVEJEUj$)MPHi~TcJm}^4tVHHueMM3MsBm>jG5|MgcKi{62vCic51C z83!FaGrdZHF%ayNNy~gLNlrSFdbJ{hyMx9ab0Ze%h$jVhG;vcsuCXy0+O4pwbhJ^%qd9)LWt;`K$NKDXCe z+`!9kXn@z^LfTOp3~q>W1o+K_gA6eEKH?pRPz2=VR(?1J9wa$|{q)Hepn zkvT5F0QymySZmR!Olip`65{AR07P@E7}hxkPDZ%(1_S;X%Zi-8Bm+b${_@lFnULR0 z){HUrP@9P9aR>_|}vMH$@GC?j2jKErrHn#fv+$GOgG^K7MC1;chZ*L8l==$t zRoLHytfY*x0_#`;@lZ(;vQC~*>ks|!V*{*Z(~lo}5DnndC7TJ(7~^`W;d=}qKJxp) z0Ne4Q0S=O!Y^~LZx`Z&fcidX0U_*;PB?E*hLTcf(Do*tq6ahw2S*GkgDMJ__+g$Z0 zDNhBuVSx+~^YS&#Hb5}MWzhjKF+~R0kfEZC@39saG(jKcin6dI%Z|a*gFp~zjE+y@ zdN2?aC5rwJQ-%My1a!F30)K^;HVz}v7iUu%DB@ zq}6=o80wCA!VC!vIu?(1)rq*Bj(IBQ^AIPWo|{^EH8jA164MUfz^tG#Sw>oTy&rX7Gdjl^5kD{|dN z8%T@E=`dv=VMccSlt{)UtJdtcU^LMnTA5o>9Rnx&%Z-#@pTC}`zo^LI%dy^M6=T$q zl5W|~R_S!og0JflvUkwb+(aVC$ilS5mgRFpcm-MQJldaeMX#H=BHGmJOUh~`Wiavh zj?y!#KPnP%7FuDH*#?NUX5oOcRUuO(%oApCJ@7wBUaE&3L){VPg9;(MV8mu2b53k| z0fDR;!t0%c!uoI@%z%kMd0p@WXipgMnRY_wL{Z`wCo6anQvL~lXeVxPaco0-_F=~N z*p0Q_A!R2fWsuN+g& zqYSsqQimg?B&VJWsr7A;>^ZW8}xf*jK|XMUe#~}usenaj!Zudx{^_A z5#7rcAjBB3(w>(PR?HpLPJri^mWt$nz;sV6=z`b8`P#cT77W!j{-Q7&=c0%ihghvb zV}}qM9=9|m3YGj;4uZg`QdY%6wiN}KBizf8hLqw;VO zc)%2#Ozbqo#8h;~%<7UP9(ropw6HjAzX>y-I*}y3e)7VH6_3Wr1qFq+0lra-ehXbK zR5+#xgv5<vFvXBU3-u6F8N?e-hdxV-8oM??uIST_fs+{a3UU~I2yrHHS~xL{Tb@p(t~)2! z1L1Hc2!%7m7PM+;=EJUsNj`z4M7A6?E?rWaBjgW0R(jMd7C>6b4{bDNo( zW2k|WeeiTSTdM4r>V$vpQZyt*&mT5t(X9>?aq`zG7)o3VFK=&Gb%can&H-UeGP)ED zPlf~GVBeuRtsIZX*Yt&7Iw^mK>}n z2i0R#Q$i^oQz{c2GU9$wg!ItYQej=I)dV;UvY*=I#q+b0=t<)PoU&mzzE1=!L$EE@ zq57W$jbH@z9`V7jpf%eaj>%oc>6FaCh%;ACCQ=kRSdxG{Ad>mVpGw&2D}EQ4bOH`@uxFedD6^ zZ1jZt43H#DuaEKkl_TuiVpRU^njqt>n^QR;^=e=z*D)oaxyHHd6_0*5qCjE<) zsG$LVa^(slCU7qs6+TFI-YI+xBfvq_XgAASp8jX%>Lx)Q|unj9lv9HANMuNwT_ClPlSOzhqZ4wpp$_2iECk958JzG zyAdEE=hh~dmxFi+H+kVe$q#2HGa9>DQ0XN{95EPlz2T4raB*0=)rvt(w}vOBdpbEZ zG{8?R-jDXbZm!G4e?tQtP@%U>PKG(}cdK9kU1&1MZAa^^j0Bg<$9R0S2n)88RzYFp}U~;m&j7;nO&IwBx#8u$?a$PWv1@}s`S()C@IeDMG;GT&e zwf1K#M^7eEO>{GED4G0R>t(AnRhAeN@%e+vv$JwJ1%!4glS(b0f!7J)0jxc5y>3@Z z^|J?zQ5jt?m7ldPFI!iMHO|>^*f!KqzbM8s`E2FK*~(#FSBRc23i;ZTqf>O%`a#EF zPJ4n$lB%0~O=xOa>&1M$rlsnY{q{ubd5Q>GcK>C(eDUt-)1O;YsbD~42q7AZHea8< zyll?J!y?xlvwQjhEWt6@5>s^Kb>V0GOR8E_t;OssO~lZgX;qNwt( zo7;*stWd$+uV)z9C&!VU4<{$)iD{ZkON%G3pEww9%J^Buq#c`))u$K1hqeJSZKGvc z*z;USx8llRrkx*ZsQ-w#0@C_QQI4vs6LW+a7I`9gxn$-D-<;c;(rbj-M46N?nHHU6 zFq;WoVGfDI|IpV~Bd8fcU(+=^H84Zrn@GBFd~7!3!EOM^!_O{FN^_B($Q~#*dYbXf zR2x|?9D}U~B*ND(>_mz#AR>y^(sY#zuQM`prn>8sWmo9bF0FLqdJkZRWm-9gR3^mkH_e!ei z*+mO|Dh8%{dQb13|Eab>?{m8V$|^2Rg@Y8Au-bepO)yV%%fkACFhCAU^wWq9?|f0% zk86CCj{Vo3y*@`@brbpTX}reidsy(<#7f8us1OVkPRlW3W=x-2REq;igSq2?Xs-N3 zL{1WRB387_y6zOQ-og%1X2r4J1u%P8!`kYbST|KuO#$c888_X z5j}e5_+6$ZQLHzAxgw809!Fo535j1rw8D(-{Rq16z#CvUsat(j1?A73jLAUqU8Yz- z;%GqR6o3IHL8+1>(_t7OGcrvJuNvC`S=a)j(Sst&3GzBW3;~B4>K7zsju1k`NMmDO>qi1TGhVHzMRYVWJGZ=& z?Qp7@pDlPFKO&8Lj!!Oh6!KbpJ%au#%b*8(RC5!t3LG%;x?#y)2mp=a^*Cj~WtrzE zBF_&Qpt=H@6D?(e@YMJw43M@f^KfOw?*aD;8jD5O=Q$Z*YCr8ot`3=A7>KOPG&2Z5 zjtJ{0)j$-{-Wt$X5FU#~xkHlbc}p3wSA~Iz2A&6$ZNaU`Y7ZGV^!k!oOu8Z1ZR2DC z)38GgHPk(lq_t)w>hdL#eFq_+HEV;Ow`VPI>ktCQ#$bRYnucdUvWI|aDsf3=scWTX}^Dci)Tw(vBCV@1;E+AQ6j2 zKS102+l^wtxAeZlppodl6};H zFt;RZ4Q0mszAD=Wh^X+*p$HWb78bl7w;PZ^fS%C-$pE#)+d>h^k3<>;>tq3tL5T&A z-dek=jQ)>`5lVnd$eC$VmA&q7!2mG??rfSN*uR(Z3V3s;8DW5UTcBAf)d$)oozy}o4z&rZ&wskb*N&j_Jq=bwDNx!`a1^kRNeQVXIzMY@k~3V9z{{SgMsvFaG0?3g_b6 z4@x_Q#sDTox-D_cn$@b=HqhIb;`nG@jXXBHUVH7;X4SQ2RE&*eV_R65VuKCBfFX#WK%k~5LQ2$2lmG`5L8Tr@ zdd?w-^j4{VK>x(PH*51F{h?Mp6xZ()iPB*ilbt6|nk#pX zO?i8>TQf5|I9Rr1NN8V)^k03z=hzu%v6NrEISc2XUL6)@oEMj+UENSgNQvUpi>s%F zf^)cpr5;74J_xaU@$tnw=l%7E$%G9a4GOSo#~)w37b5~l>tMg#-k;1^H@PIGQ7rFY zlyV0NpA@M9(oS@7=H`I{J@y4b1*3ZPoxE`bl5G}Ly<93?# z0y~EJYsgh!TF7-1^C0A{AK}~0+{I}(0kkeTP&VyOwoC2Jx`8uquy%9!na6_jMH@ga1!YyWPfU8VC@D_Bu0ts-hT+l5l`l{4{4#!S$d5^NI4sjbuBwuuYV!+*2OK`v4k)mf|7zCh@LbAV; zVg4pmsnORv0Ne|+0XGk7a2tSn>gNFn;;$G4ATlBW7~DIl-xW~U-Fiy3!2kdw?=}HJ zqTCNhga&8#Yjj(IwCP16+_c*BEz2+r+g6`>&OmVkOon;LMCd_Ij>5+@5}|@vTHBMk z{k?8R^j9rB`bO3EHgl&nA0$l+0JS@Lk(=!L&=0@}qOddi@j@=2FaThXb4F&nytz*{ zTZr8M1?F-6$1o3>kPudDFDL&0=wyQoZrUsCI7chr`4Xfcl)t;M^y108_aBe9Ef4`x zZLb_Tg@V&8?OJ%T?iwfjgSi(^&d%OBvnbOErh=6y<4oLIIJcPqalcOspMhhT2TO?h zoFFr!1B`ec7~3n3IgXAN_w3^`V}aWj>wy#97o1}T?WXzoe+gU5x0)fQ-R(7KLRWga%n5|QVFA*bx{wP`e_{;U)mx5uK z2TBOFCj}GrC}I?V)lDZIB|$+Ioe<>M31k!~Eu~ZWh|M(rv2N+;}n>CgL+kUE2Gn^skn&#L?MxcT7q#vOVY-=Ha4u z|N5I-n17=ZfQgl}PBeybg-$Fpd$bM8QxXfnC=teX0ny5wZj}Vx%b9N|*3ch~p6!*4 z6jB)VBeyz1{%lk?Mo;GGh#{y=CxynRr_}TS=K2Xbbs&^pN35PNAnUoQyUuQL+Pukt<*T(#%+x3djWQUynfI(=S+AryH)D zJ*oq524t8A1_dC<)`yvBIR5|d3a5e0r?Ye_hLEh(e1=v(0K0Y*fL~8z0M-p56dW`g z)hSA~>FF3*Y9;2{e0{DO4QtbLI2NzW89@L#DO^=ahBr{C-*|@s=qb;WiMg}R+jOdv zN;R%`<(oHxVVH*nAlo0XP>FssA_15wq(j z`qGX$kqq7eMj!VQQg;>p9zD+p>QCclrDOvI;6XD@Lhe{>BNp$xIJLk-_oAFHm)2j- zG_pUgKfmzA8?FEu=Ai*drsSaccvLDtwMVM@jpP)r`Bs1K_d8GIwy*?5)3$zw%_*Rn z*wrJO(v6OiN%z^NAv`=1r#27Wz;quo8_k*xh>_{xL>++B(ZbRXDmZcMgF%ZRFDPS$ z7?G1U3^Pmsaj=**%@GMe0hwdRjp+!B&aBi85=`{<*;TCfQttcjixAA*{q(+Jrf`2t zwThy98$f9N{bN*jBQf_d2fB9wHKwoN4UC1SQ*JZo>m^@WTv2xB`j=n2lJ`P0%mam* zg}0kK?#Kin{BXSG>g~;sdvz^})t2b>pLbl>OrIRC)THo8@h9MiPuE$uTCMJEEz|*& zR>l~{{xj2j`_?QRO&TKbi`mBHoYQbqDYLLlM?nakmUiaT^~djcBQ(Q2RLBffJa1;R zt{4fFvJ&O>#pUJZ>?Y=)wqsEf%;ILRdDuLf&1E1ky?7B}WnQfuJzHE{$z|px;CsC{ z_$E)T=G!mV)_RjggO+x+sLU|xZTu^j*IuG$8wU{(h_C!sYa~RA3^T;|d(U4t#Yo@@ z1(ug-A>?lHR9^>S8<|>dyPhz>Bl}e#MwQU2*Xym84|o*-CSZgNSwNcTu`v)D{eO6Q z9+{u_MF42WAg_Ed%!B6(;rp#^F%sybzgm710swM|^#QPLkO0&=g;J6JtA~N)+X1A5 z#uzoC91V$eZ~ppF4v|6AGbt_}d)=7V9Fp+t9< zk|IENM0rD^Qwb7u<@cE`J0kQDT@xY#e?A8w!!SdLl0vjRrFScbaA6}vEm0&GMk1p? zVwHCzPYp@%akXS1)7JqA5+LJyfbiok!!W~z0_%tKPghr;a$O}Q1>l=E5j?n--(UIu z>eG2`{?63I_UY`^)ra$%PcwDpC?V4=tt`!!_A-_MAQ*;Wh6gapbIwVplP)w%d0Pr9 z|01GTYC1daNyoX&6|H`QORLyEdXjB4vd-nCB`5&D5K`tB)@DrC-8oz;84F%0vdp>2G%%StpnF*P+YF`jbTt5OOjjPi0iK8`ZLSiD+& zw&z7C0IkhuW<0#Hu`wPtE3YR(3ZV>R0fo|!9zFVMe5&d$)i?kdh8Y$x*8KBA1;x!% zD7JqTPNU^dg0vI8nRqO`F^=NDiD+SVm-Yk7V$V&7ryo7SRv)R3Q?f8A!7k37p!*L& znU?7+c!C3vVVI#|7ww0nIMq%+bGd9w`Q=!Cu@b`o44lsHQ6A7zT=az}01O`-WJOdXWWlh=8v7f_lm=3g@W=xcQUxl0+rFbp#+$l`1v9iugnB=#`s zTp!o~UykiARZs^VcurI9QqUABf0<3krf~>cAen{BqVxoy{JBCCQ2;$XZDwC`05S|S z6odpK)q>ObCQO#+xLG*+ep?zcBooK)&8UvLjZ~*lN?2N+KYNy%p2nQSB@grcb4%iw zU0gb;M8Bf8AhA@#Xn3% zImeA|B$KJs$>BvY2tesyJgubSQDo-m)RIAd7E6!Kcq|#l`tkMTDj$Yp7-lHwSAh0v zd2XvhJzpgC5+j{@rXFNP?Ha!g85Qf`OSs(^-^xv z5(9jD_1pG(Z)JTyZ*l%H3^QcNFQsLvgzC2giRfsQQi8G!MT+QKz%Z2W-_yR3F9xqp z7y!Ot7={@#Fz*0_5}rrjYsm}X7kUx~CGG*>i4fsM?p`k?tDhN$8CF#PWZb=7(w`mX z@F*Y!DN%3j(1PaS5I!|?zYm6ChLcF}m2~(wVrVi$-|LqgWL}Vfg8&Q!`ZyNi0Av_u zD53X#03agBjs00j`axfJPd|tR0f>b3%s~ecoIrAD?)kFwAfPzOpitz2(Qw;aa}=y3%=CDuO2%hGB*P9e~zqap7k(oWx_l@rjwa`l=;DAnp1rD)D1+v|*7a ze^pX9bfdm#j>lqJ5@R;|0f>W*6q*BL;dIA&?tLl#19<---zL6I%x~=7O-~a+90u^o z{JUnRv#;H5vox|z8(UkFB5i0WT8csd3zbj;0)obvXuKHTP6iJilz1}n;K92ez>nxm z>+989H_tC&S*ATdnPq05`JVtR`p&}9HJxbJuiHYX!hFtQmKFN7;XS2C#bC~RquXHg zcP(X54?TS}TW~6TjSfzSq8D%*%DKznOU6Bmmcr zLzW>&KR^dUbO37c(6ppd7~Vf>2O?k5A!q{dHVR>j*#qm6LAjSiZd`a$UFLF@NzSRWcv6maFkTy;c6=Itk=NgTYc(2s z`LtR_=ClKY*`ZgE@) zGr@`cT&dpaZv1II;GAF9JqVXB>))2eCM?quy?t1FdAKh_$p8SRgATy#&ob2UQAp!^ zV=_QG@50Ocuc}ka;y-Wx=}I*TN`xi^0GKY>pT9B&(}+yYP2!Nu-S`6lfD|GriDdSl zkQ?9Yp-K870RbST_;|W!aYi(LuKg2s7Uz62AG8pF0FXjiW@$|NGFS4)fBt$f=G@O| zK5>-Y13)U7#Sc!U+Qa_R;=F0KCxg!#J&3yonO4%$?Zegk52CiFp z|9YQu&NPzE+IgX@U7WovVRs7vq*1ZxJGGT-=Cxnt_CA=CKC(<}r>>~rid%kv zqr?CJQi%NHtyr1nm-fAOR2Wb9h&C41Vm+jUt=vavheaIq0RSmP&U_(J!ql9)$}KN- ziAhP#^@cLU+(5XE#S$?rg8)G4Q0eol&B)8=D}fHY^2#yEq71v|@y`I98w5#z7t0_3 zkU~^?`>GXrIsrXlo1RlnL}C3bZvP3uwJu>D1OU>9n(ME6Isk=ci}>h%NQ}bHdGFT@ za4v}ZUCaOhAcaW2e%w<+H#*xQU%9iCyTmOU3K2DKmk@vekVgLQYS|To zynV%1?$M4zmSvG#*osZhbABM)*7F4)0SEwTM6z}kM?wqS;jNrAu7DwbXJs#LN0lIH zynpRu5f}hciDX;KH|FOb z*1Aq8r5x8hv(Rbotgo-%*y;LM0S17SGS$yMX^e#-$yqrWq%d@RM>-S|%c2eo0MbfI zHbKU(L(_>k{W?DwlRyASsp2I3Bpb_QO+kLPs4x2FMJ9_0AONIRcKq{O#+(-SVzTVP zrA|RK;sZd6S=?fl^(z347drkZB*)aFq7feeQj6s5&3A`S-<2eB^WVQ0mY((T<>h95 z;jpu>#}I%3kW$iV)>cn0E)M!P>LKSk0AIYwW|r%>mR3#*2j{hYUt%9L0HjccF`kpnKZ06+?5vwU{G;0mph&&O-k4Q_G9!se4b zrNm(11?@YhU52B90U&)coDL6qK~QO5jVgA~xSivi7%kTdktgP^4?O+J@byguAONIK zhRr@~B;K6J3n6CgWUC%>;+L9FV$Tx;A@o)4^=~pPgaAPLSU2CALg;~~qt3S7;lelo z>uZ~y88iu~;*|~>p#dO;tliaC;=PH2N?zFF&B-_bH|lGxKnPC=+w%hVY}a7`0I9=s z>y0D`gb6_9md|!M5x-RWTy~Wy$)6EXQeG_i3;-Z?xU+N8b8R8=`M@oI`�}Irm>* z#L6}X(dI?X!4oh5q!4XvoQ@Kq15l{=)!V1Dj7$RVes0AoKO;Qv&7*$DK_xx_q!3*S z>!*d58$_=A;mcu>WzE~boNuodRQ_ruiARgujzjb?^iX0I5WbE?sg;$JOdWx9CsGu=JI4?lUs&imc>V=|6tmbY zIrQr|kgT~7w=Bka=wJsRz>oIsAO(RKhQjcpX@v|3rS1yiN^v!~^a6rM5ZrhN7oNr2 zY7v|}wEqXWLI{Mspm*+)YMqp%R`v@9l+*tJjA40x8PsB-xc~+*j_FHHR7^1@0YK+$ zvo0mO`Sc$Gg9WM95L^ { + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(SetupGuideLayout)).toHaveLength(1); + expect(wrapper.find(SetPageChrome)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.tsx new file mode 100644 index 00000000000000..fcb3b399c75b0e --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.tsx @@ -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 React from 'react'; +import { EuiSpacer, EuiTitle, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; + +import { ENTERPRISE_SEARCH_PLUGIN } from '../../../../../common/constants'; +import { SetupGuide as SetupGuideLayout } from '../../../shared/setup_guide'; +import { SetEnterpriseSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; +import { SendEnterpriseSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; +import GettingStarted from './assets/getting_started.png'; + +export const SetupGuide: React.FC = () => ( + + + + + + {i18n.translate('xpack.enterpriseSearch.enterpriseSearch.setupGuide.videoAlt', + + + +

    + +

    +
    + + +

    + +

    +
    + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx index b2918dac086f6c..2c0902163e3d6a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import '../__mocks__/shallow_usecontext.mock'; + +import React, { useContext } from 'react'; import { shallow } from 'enzyme'; import { EuiPage } from '@elastic/eui'; @@ -12,54 +14,31 @@ import '../__mocks__/kea.mock'; import { useValues } from 'kea'; import { EnterpriseSearch } from './'; +import { SetupGuide } from './components/setup_guide'; import { ErrorConnecting } from './components/error_connecting'; -import { ProductCard } from './components/product_card'; +import { ProductSelector } from './components/product_selector'; describe('EnterpriseSearch', () => { beforeEach(() => { (useValues as jest.Mock).mockReturnValue({ errorConnecting: false }); + (useContext as jest.Mock).mockImplementationOnce(() => ({ config: { host: 'localhost' } })); }); - it('renders the overview page and product cards', () => { - const wrapper = shallow( - - ); + it('renders the Setup Guide and Product Selector', () => { + const wrapper = shallow(); - expect(wrapper.find(EuiPage).hasClass('enterpriseSearchOverview')).toBe(true); - expect(wrapper.find(ProductCard)).toHaveLength(2); + expect(wrapper.find(SetupGuide)).toHaveLength(1); + expect(wrapper.find(ProductSelector)).toHaveLength(1); }); - it('renders the error connecting prompt', () => { + it('renders the error connecting prompt when host is not configured', () => { (useValues as jest.Mock).mockReturnValueOnce({ errorConnecting: true }); + (useContext as jest.Mock).mockImplementationOnce(() => ({ config: { host: '' } })); + const wrapper = shallow(); expect(wrapper.find(ErrorConnecting)).toHaveLength(1); expect(wrapper.find(EuiPage)).toHaveLength(0); - }); - - describe('access checks', () => { - it('does not render the App Search card if the user does not have access to AS', () => { - const wrapper = shallow( - - ); - - expect(wrapper.find(ProductCard)).toHaveLength(1); - expect(wrapper.find(ProductCard).prop('product').ID).toEqual('workplaceSearch'); - }); - - it('does not render the Workplace Search card if the user does not have access to WS', () => { - const wrapper = shallow( - - ); - - expect(wrapper.find(ProductCard)).toHaveLength(1); - expect(wrapper.find(ProductCard).prop('product').ID).toEqual('appSearch'); - }); - - it('does not render any cards if the user does not have access', () => { - const wrapper = shallow(); - - expect(wrapper.find(ProductCard)).toHaveLength(0); - }); + expect(wrapper.find(ProductSelector)).toHaveLength(0); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.tsx index 3a3ba02e07058a..e2c05434dd0bbc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.tsx @@ -4,81 +4,37 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useContext } from 'react'; +import { Route, Switch } from 'react-router-dom'; import { useValues } from 'kea'; -import { - EuiPage, - EuiPageBody, - EuiPageHeader, - EuiPageHeaderSection, - EuiPageContentBody, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiTitle, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; +import { KibanaContext, IKibanaContext } from '../index'; import { IInitialAppData } from '../../../common/types'; -import { APP_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN } from '../../../common/constants'; import { HttpLogic } from '../shared/http'; -import { SetEnterpriseSearchChrome as SetPageChrome } from '../shared/kibana_chrome'; -import { SendEnterpriseSearchTelemetry as SendTelemetry } from '../shared/telemetry'; + +import { ROOT_PATH, SETUP_GUIDE_PATH } from './routes'; import { ErrorConnecting } from './components/error_connecting'; -import { ProductCard } from './components/product_card'; +import { ProductSelector } from './components/product_selector'; +import { SetupGuide } from './components/setup_guide'; -import AppSearchImage from './assets/app_search.png'; -import WorkplaceSearchImage from './assets/workplace_search.png'; import './index.scss'; export const EnterpriseSearch: React.FC = ({ access = {} }) => { const { errorConnecting } = useValues(HttpLogic); - const { hasAppSearchAccess, hasWorkplaceSearchAccess } = access; - - return errorConnecting ? ( - - ) : ( - - - - - - - - -

    - {i18n.translate('xpack.enterpriseSearch.overview.heading', { - defaultMessage: 'Welcome to Elastic Enterprise Search', - })} -

    -
    - -

    - {i18n.translate('xpack.enterpriseSearch.overview.subheading', { - defaultMessage: 'Select a product to get started', - })} -

    -
    -
    -
    - - - {hasAppSearchAccess && ( - - - - )} - {hasWorkplaceSearchAccess && ( - - - - )} - - - -
    -
    + const { config } = useContext(KibanaContext) as IKibanaContext; + + const showErrorConnecting = config.host && errorConnecting; + + return ( + + + + + + {showErrorConnecting ? : } + + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/routes.ts new file mode 100644 index 00000000000000..1f9c06e9683ab0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/routes.ts @@ -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 const ROOT_PATH = '/'; +export const SETUP_GUIDE_PATH = '/setup_guide'; From d4232c5b028c6b06232c8f43a85c25f312d7911a Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 23 Sep 2020 22:40:03 -0700 Subject: [PATCH 30/35] skip security solution tests that are preventing es snapshot promotion (#78366) Co-authored-by: spalger --- .../apis/epm/install_remove_assets.ts | 3 ++- .../apis/epm/update_assets.ts | 3 ++- .../apps/endpoint/endpoint_list.ts | 9 +++++---- .../apis/artifacts/index.ts | 1 + .../security_solution_endpoint_api_int/apis/metadata.ts | 1 + 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts index 198c129b7482fa..492af399d5e308 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts @@ -29,7 +29,8 @@ export default function (providerContext: FtrProviderContext) { .send({ force: true }); }; - describe('installs and uninstalls all assets', async () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/72102 + describe.skip('installs and uninstalls all assets', async () => { describe('installs all assets when installing a package for the first time', async () => { skipIfNoDockerRegistry(providerContext); before(async () => { diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/update_assets.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/update_assets.ts index 9af27f5f985582..8203b4d1838716 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/epm/update_assets.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/update_assets.ts @@ -32,7 +32,8 @@ export default function (providerContext: FtrProviderContext) { .send({ force: true }); }; - describe('updates all assets when updating a package to a different version', async () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/72102 + describe.skip('updates all assets when updating a package to a different version', async () => { skipIfNoDockerRegistry(providerContext); before(async () => { await installPackage(pkgKey); diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts index d46171bbaa49f5..c9d2b7a21d0da3 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts @@ -65,7 +65,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ], ]; - describe('endpoint list', function () { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/72102 + describe.skip('endpoint list', function () { this.tags('ciGroup7'); const sleep = (ms = 100) => new Promise((resolve) => setTimeout(resolve, ms)); @@ -86,7 +87,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.exists('emptyPolicyTable'); }); - it.skip('finds data after load and polling', async () => { + it('finds data after load and polling', async () => { await esArchiver.load('endpoint/metadata/destination_index', { useCreate: true }); await pageObjects.endpoint.waitForTableToHaveData('endpointListTable', 1100); const tableData = await pageObjects.endpointPageUtils.tableData('endpointListTable'); @@ -94,7 +95,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - describe.skip('when there is data,', () => { + describe('when there is data,', () => { before(async () => { await esArchiver.load('endpoint/metadata/destination_index', { useCreate: true }); await pageObjects.endpoint.navigateToEndpointList(); @@ -212,7 +213,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - describe.skip('displays the correct table data for the kql queries', () => { + describe('displays the correct table data for the kql queries', () => { before(async () => { await esArchiver.load('endpoint/metadata/destination_index', { useCreate: true }); await pageObjects.endpoint.navigateToEndpointList(); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/artifacts/index.ts b/x-pack/test/security_solution_endpoint_api_int/apis/artifacts/index.ts index 6c225dea5430f8..5a4053ee6f0a9a 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/artifacts/index.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/artifacts/index.ts @@ -18,6 +18,7 @@ export default function (providerContext: FtrProviderContext) { const supertestWithoutAuth = getSupertestWithoutAuth(providerContext); let agentAccessAPIKey: string; + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/72102 describe.skip('artifact download', () => { before(async () => { await esArchiver.load('endpoint/artifacts/api_feature', { useCreate: true }); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts index d1e98876596e50..2ab12e1ff3aae3 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts @@ -23,6 +23,7 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/72102 describe.skip('test metadata api', () => { describe(`POST ${METADATA_REQUEST_ROUTE} when index is empty`, () => { it('metadata api should return empty result when index is empty', async () => { From 9b1883d51e85ccda89be42e74368d3b9de69866c Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 23 Sep 2020 23:30:43 -0700 Subject: [PATCH 31/35] skip flaky suite (#78375) --- .../test/security_solution_endpoint/apps/endpoint/resolver.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts index 620eab37f9b46e..3e9726bf40073d 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/resolver.ts @@ -13,7 +13,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const queryBar = getService('queryBar'); - describe('Endpoint Event Resolver', function () { + // FLAKY: https://github.com/elastic/kibana/issues/78375 + describe.skip('Endpoint Event Resolver', function () { before(async () => { await esArchiver.load('endpoint/resolver_tree', { useCreate: true }); await pageObjects.hosts.navigateToSecurityHostsPage(); From c02e42ad01d35753e595c89056df56393bc19c2d Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 23 Sep 2020 23:35:45 -0700 Subject: [PATCH 32/35] skip flaky suite (#78373) --- test/functional/apps/discover/_doc_navigation.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/apps/discover/_doc_navigation.js b/test/functional/apps/discover/_doc_navigation.js index 5ae799f8756c09..31aef96918ffab 100644 --- a/test/functional/apps/discover/_doc_navigation.js +++ b/test/functional/apps/discover/_doc_navigation.js @@ -28,8 +28,8 @@ export default function ({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const retry = getService('retry'); - // Flaky: https://github.com/elastic/kibana/issues/71216 - describe('doc link in discover', function contextSize() { + // FLAKY: https://github.com/elastic/kibana/issues/78373 + describe.skip('doc link in discover', function contextSize() { beforeEach(async function () { log.debug('load kibana index with default index pattern'); await esArchiver.loadIfNeeded('discover'); From 477f6a182ed4d4f97b4555c76a73781f788d22a2 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 24 Sep 2020 09:10:00 +0200 Subject: [PATCH 33/35] [CSM] Fix pie chart legend (#78253) --- .../Charts/VisitorBreakdownChart.tsx | 11 +++- .../app/RumDashboard/RumDashboard.tsx | 18 +++--- .../lib/rum_client/get_page_view_trends.ts | 4 +- .../lib/rum_client/get_visitor_breakdown.ts | 60 ++++++++++++------- 4 files changed, 59 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/VisitorBreakdownChart.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/VisitorBreakdownChart.tsx index 34fcf62178711b..dea6525d4be5fe 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/VisitorBreakdownChart.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/Charts/VisitorBreakdownChart.tsx @@ -10,6 +10,7 @@ import { DARK_THEME, Datum, LIGHT_THEME, + PartialTheme, Partition, PartitionLayout, Settings, @@ -34,6 +35,12 @@ interface Props { loading: boolean; } +const theme: PartialTheme = { + legend: { + verticalWidth: 100, + }, +}; + export function VisitorBreakdownChart({ loading, options }: Props) { const [darkMode] = useUiSetting$('theme:darkMode'); @@ -42,13 +49,13 @@ export function VisitorBreakdownChart({ loading, options }: Props) { : EUI_CHARTS_THEME_LIGHT; return ( - + - - - + + + - - + + + + - - - + + + ); diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts b/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts index ef4f8b16e0e7b5..352a3ecdc3f12b 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts @@ -46,7 +46,7 @@ export async function getPageViewTrends({ terms: { field: breakdownItem.fieldName, size: 9, - missing: 'Other', + missing: 'Others', }, }, } @@ -103,7 +103,7 @@ export async function getPageViewTrends({ }); // Top 9 plus others, get a diff from parent bucket total if (bCount > top9Count) { - res.Other = bCount - top9Count; + res.Others = bCount - top9Count; } } diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts b/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts index 1b4388afd7c5d1..7345d6acc0f82b 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts @@ -12,7 +12,6 @@ import { SetupUIFilters, } from '../helpers/setup_request'; import { - USER_AGENT_DEVICE, USER_AGENT_NAME, USER_AGENT_OS, } from '../../../common/elasticsearch_fieldnames'; @@ -32,6 +31,7 @@ export async function getVisitorBreakdown({ const params = mergeProjection(projection, { body: { size: 0, + track_total_hits: true, query: { bool: projection.body.query.bool, }, @@ -39,19 +39,13 @@ export async function getVisitorBreakdown({ browsers: { terms: { field: USER_AGENT_NAME, - size: 10, + size: 9, }, }, os: { terms: { field: USER_AGENT_OS, - size: 10, - }, - }, - devices: { - terms: { - field: USER_AGENT_DEVICE, - size: 10, + size: 9, }, }, }, @@ -61,20 +55,42 @@ export async function getVisitorBreakdown({ const { apmEventClient } = setup; const response = await apmEventClient.search(params); - const { browsers, os, devices } = response.aggregations!; + const { browsers, os } = response.aggregations!; + + const totalItems = response.hits.total.value; + + const browserTotal = browsers.buckets.reduce( + (prevVal, item) => prevVal + item.doc_count, + 0 + ); + + const osTotal = os.buckets.reduce( + (prevVal, item) => prevVal + item.doc_count, + 0 + ); + + const browserItems = browsers.buckets.map((bucket) => ({ + count: bucket.doc_count, + name: bucket.key as string, + })); + + browserItems.push({ + count: totalItems - browserTotal, + name: 'Others', + }); + + const osItems = os.buckets.map((bucket) => ({ + count: bucket.doc_count, + name: bucket.key as string, + })); + + osItems.push({ + count: totalItems - osTotal, + name: 'Others', + }); return { - browsers: browsers.buckets.map((bucket) => ({ - count: bucket.doc_count, - name: bucket.key as string, - })), - os: os.buckets.map((bucket) => ({ - count: bucket.doc_count, - name: bucket.key as string, - })), - devices: devices.buckets.map((bucket) => ({ - count: bucket.doc_count, - name: bucket.key as string, - })), + os: osItems, + browsers: browserItems, }; } From 62ddaa9e205ff49d4deb9912025e510513c49ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Thu, 24 Sep 2020 09:36:22 +0200 Subject: [PATCH 34/35] [Security Solution] Cleanup IP Details graphql (#78318) --- .../security_solution/network/users/index.ts | 6 +- .../public/graphql/introspection.json | 1008 ++++------------- .../security_solution/public/graphql/types.ts | 398 +------ .../components/users_table/columns.tsx | 12 +- .../network/components/users_table/mock.ts | 5 +- .../containers/details/index.gql_query.ts | 91 -- .../containers/users/index.gql_query.ts | 59 - .../security_solution/server/graphql/index.ts | 2 - .../server/graphql/ip_details/index.ts | 8 - .../server/graphql/ip_details/resolvers.ts | 50 - .../server/graphql/ip_details/schema.gql.ts | 97 -- .../security_solution/server/graphql/types.ts | 409 +------ .../security_solution/server/init_server.ts | 2 - .../server/lib/compose/kibana.ts | 2 - .../ip_details/elasticsearch_adapter.test.ts | 53 - .../lib/ip_details/elasticsearch_adapter.ts | 160 --- .../server/lib/ip_details/index.ts | 37 - .../server/lib/ip_details/mock.ts | 430 ------- .../lib/ip_details/query_overview.dsl.ts | 126 --- .../server/lib/ip_details/query_users.dsl.ts | 104 -- .../server/lib/ip_details/types.ts | 135 --- .../security_solution/server/lib/types.ts | 2 - .../apis/security_solution/index.js | 2 +- .../apis/security_solution/network_details.ts | 2 + .../apis/security_solution/users.ts | 3 + 25 files changed, 258 insertions(+), 2945 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/network/containers/details/index.gql_query.ts delete mode 100644 x-pack/plugins/security_solution/public/network/containers/users/index.gql_query.ts delete mode 100644 x-pack/plugins/security_solution/server/graphql/ip_details/index.ts delete mode 100644 x-pack/plugins/security_solution/server/graphql/ip_details/resolvers.ts delete mode 100644 x-pack/plugins/security_solution/server/graphql/ip_details/schema.gql.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/ip_details/elasticsearch_adapter.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/ip_details/elasticsearch_adapter.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/ip_details/index.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/ip_details/mock.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/ip_details/query_overview.dsl.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/ip_details/query_users.dsl.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/ip_details/types.ts diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/users/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/users/index.ts index 196317e7587bf0..8c4e19a804148d 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/users/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/users/index.ts @@ -40,9 +40,9 @@ export interface NetworkUsersNode { export interface NetworkUsersItem { name?: Maybe; - id?: Maybe; - groupId?: Maybe; - groupName?: Maybe; + id?: Maybe; + groupId?: Maybe; + groupName?: Maybe; count?: Maybe; } diff --git a/x-pack/plugins/security_solution/public/graphql/introspection.json b/x-pack/plugins/security_solution/public/graphql/introspection.json index 2f312c461ff8cc..ece07124143497 100644 --- a/x-pack/plugins/security_solution/public/graphql/introspection.json +++ b/x-pack/plugins/security_solution/public/graphql/introspection.json @@ -1245,174 +1245,6 @@ "isDeprecated": false, "deprecationReason": null }, - { - "name": "IpOverview", - "description": "", - "args": [ - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "ip", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "defaultIndex", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - } - }, - "defaultValue": null - }, - { - "name": "docValueFields", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "docValueFieldsInput", - "ofType": null - } - } - } - }, - "defaultValue": null - } - ], - "type": { "kind": "OBJECT", "name": "IpOverviewData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "Users", - "description": "", - "args": [ - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "ip", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "pagination", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "PaginationInputPaginated", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "sort", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "UsersSortField", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "flowTarget", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "FlowTarget", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "defaultIndex", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "UsersData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, { "name": "KpiNetwork", "description": "", @@ -6170,594 +6002,32 @@ { "name": "ip", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "LastEventTimeData", - "description": "", - "fields": [ - { - "name": "lastSeen", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "HostsSortField", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "field", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "HostsFields", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "direction", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "Direction", "ofType": null } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "HostsFields", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "hostName", - "description": "", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "lastSeen", - "description": "", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "HostsData", - "description": "", - "fields": [ - { - "name": "edges", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "HostsEdges", "ofType": null } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "totalCount", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "pageInfo", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "PageInfoPaginated", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "HostsEdges", - "description": "", - "fields": [ - { - "name": "node", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "HostItem", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "cursor", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "HostItem", - "description": "", - "fields": [ - { - "name": "_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "cloud", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "CloudFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "endpoint", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "EndpointFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "host", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "HostEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "lastSeen", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CloudFields", - "description": "", - "fields": [ - { - "name": "instance", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "CloudInstance", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "machine", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "CloudMachine", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "provider", - "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "region", - "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CloudInstance", - "description": "", - "fields": [ - { - "name": "id", - "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CloudMachine", - "description": "", - "fields": [ - { - "name": "type", - "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "EndpointFields", - "description": "", - "fields": [ - { - "name": "endpointPolicy", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "sensorVersion", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "policyStatus", - "description": "", - "args": [], - "type": { "kind": "ENUM", "name": "HostPolicyResponseActionStatus", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "HostPolicyResponseActionStatus", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "success", - "description": "", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "failure", - "description": "", - "isDeprecated": false, - "deprecationReason": null - }, - { "name": "warning", "description": "", "isDeprecated": false, "deprecationReason": null } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "FirstLastSeenHost", - "description": "", - "fields": [ - { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "firstSeen", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "lastSeen", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "IpOverviewData", - "description": "", - "fields": [ - { - "name": "client", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Overview", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "destination", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Overview", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "host", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "HostEcsFields", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "server", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Overview", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "source", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Overview", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Overview", - "description": "", - "fields": [ - { - "name": "firstSeen", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "lastSeen", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "autonomousSystem", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "AutonomousSystem", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "geo", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "GeoEcsFields", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "AutonomousSystem", - "description": "", - "fields": [ - { - "name": "number", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "organization", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "AutonomousSystemOrganization", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "AutonomousSystemOrganization", + "name": "LastEventTimeData", "description": "", "fields": [ { - "name": "name", + "name": "lastSeen", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "inspect", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -6769,7 +6039,7 @@ }, { "kind": "INPUT_OBJECT", - "name": "UsersSortField", + "name": "HostsSortField", "description": "", "fields": null, "inputFields": [ @@ -6779,7 +6049,7 @@ "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "ENUM", "name": "UsersFields", "ofType": null } + "ofType": { "kind": "ENUM", "name": "HostsFields", "ofType": null } }, "defaultValue": null }, @@ -6800,40 +6070,30 @@ }, { "kind": "ENUM", - "name": "UsersFields", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { "name": "name", "description": "", "isDeprecated": false, "deprecationReason": null }, - { "name": "count", "description": "", "isDeprecated": false, "deprecationReason": null } - ], - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "FlowTarget", + "name": "HostsFields", "description": "", "fields": null, "inputFields": null, "interfaces": null, "enumValues": [ - { "name": "client", "description": "", "isDeprecated": false, "deprecationReason": null }, { - "name": "destination", + "name": "hostName", "description": "", "isDeprecated": false, "deprecationReason": null }, - { "name": "server", "description": "", "isDeprecated": false, "deprecationReason": null }, - { "name": "source", "description": "", "isDeprecated": false, "deprecationReason": null } + { + "name": "lastSeen", + "description": "", + "isDeprecated": false, + "deprecationReason": null + } ], "possibleTypes": null }, { "kind": "OBJECT", - "name": "UsersData", + "name": "HostsData", "description": "", "fields": [ { @@ -6849,7 +6109,7 @@ "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "UsersEdges", "ofType": null } + "ofType": { "kind": "OBJECT", "name": "HostsEdges", "ofType": null } } } }, @@ -6896,7 +6156,7 @@ }, { "kind": "OBJECT", - "name": "UsersEdges", + "name": "HostsEdges", "description": "", "fields": [ { @@ -6906,7 +6166,7 @@ "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "UsersNode", "ofType": null } + "ofType": { "kind": "OBJECT", "name": "HostItem", "ofType": null } }, "isDeprecated": false, "deprecationReason": null @@ -6931,7 +6191,7 @@ }, { "kind": "OBJECT", - "name": "UsersNode", + "name": "HostItem", "description": "", "fields": [ { @@ -6943,18 +6203,116 @@ "deprecationReason": null }, { - "name": "timestamp", + "name": "cloud", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "CloudFields", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "endpoint", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "EndpointFields", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "host", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "HostEcsFields", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "inspect", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "lastSeen", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, "isDeprecated": false, "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CloudFields", + "description": "", + "fields": [ + { + "name": "instance", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "CloudInstance", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "user", + "name": "machine", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "CloudMachine", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "provider", + "description": "", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "region", + "description": "", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CloudInstance", + "description": "", + "fields": [ + { + "name": "id", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "UsersItem", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null } @@ -6966,11 +6324,34 @@ }, { "kind": "OBJECT", - "name": "UsersItem", + "name": "CloudMachine", "description": "", "fields": [ { - "name": "name", + "name": "type", + "description": "", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "EndpointFields", + "description": "", + "fields": [ + { + "name": "endpointPolicy", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "String", "ofType": null }, @@ -6978,34 +6359,77 @@ "deprecationReason": null }, { - "name": "id", + "name": "sensorVersion", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "groupId", + "name": "policyStatus", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "ENUM", "name": "HostPolicyResponseActionStatus", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "HostPolicyResponseActionStatus", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "success", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "failure", + "description": "", "isDeprecated": false, "deprecationReason": null }, + { "name": "warning", "description": "", "isDeprecated": false, "deprecationReason": null } + ], + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "FirstLastSeenHost", + "description": "", + "fields": [ { - "name": "groupName", + "name": "inspect", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "count", + "name": "firstSeen", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "lastSeen", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -12242,6 +11666,26 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "ENUM", + "name": "FlowTarget", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { "name": "client", "description": "", "isDeprecated": false, "deprecationReason": null }, + { + "name": "destination", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "server", "description": "", "isDeprecated": false, "deprecationReason": null }, + { "name": "source", "description": "", "isDeprecated": false, "deprecationReason": null } + ], + "possibleTypes": null + }, { "kind": "ENUM", "name": "FlowDirection", diff --git a/x-pack/plugins/security_solution/public/graphql/types.ts b/x-pack/plugins/security_solution/public/graphql/types.ts index bcb580a1a2988e..1083583cb133c2 100644 --- a/x-pack/plugins/security_solution/public/graphql/types.ts +++ b/x-pack/plugins/security_solution/public/graphql/types.ts @@ -73,12 +73,6 @@ export interface HostsSortField { direction: Direction; } -export interface UsersSortField { - field: UsersFields; - - direction: Direction; -} - export interface NetworkTopTablesSortField { field: NetworkTopTablesFields; @@ -309,18 +303,6 @@ export enum HostPolicyResponseActionStatus { warning = 'warning', } -export enum UsersFields { - name = 'name', - count = 'count', -} - -export enum FlowTarget { - client = 'client', - destination = 'destination', - server = 'server', - source = 'source', -} - export enum HistogramType { authentications = 'authentications', anomalies = 'anomalies', @@ -410,6 +392,13 @@ export enum NetworkHttpFields { statuses = 'statuses', } +export enum FlowTarget { + client = 'client', + destination = 'destination', + server = 'server', + source = 'source', +} + export enum FlowDirection { uniDirectional = 'uniDirectional', biDirectional = 'biDirectional', @@ -535,10 +524,6 @@ export interface Source { HostFirstLastSeen: FirstLastSeenHost; - IpOverview?: Maybe; - - Users: UsersData; - KpiNetwork?: Maybe; KpiHosts: KpiHostsData; @@ -1462,76 +1447,6 @@ export interface FirstLastSeenHost { lastSeen?: Maybe; } -export interface IpOverviewData { - client?: Maybe; - - destination?: Maybe; - - host: HostEcsFields; - - server?: Maybe; - - source?: Maybe; - - inspect?: Maybe; -} - -export interface Overview { - firstSeen?: Maybe; - - lastSeen?: Maybe; - - autonomousSystem: AutonomousSystem; - - geo: GeoEcsFields; -} - -export interface AutonomousSystem { - number?: Maybe; - - organization?: Maybe; -} - -export interface AutonomousSystemOrganization { - name?: Maybe; -} - -export interface UsersData { - edges: UsersEdges[]; - - totalCount: number; - - pageInfo: PageInfoPaginated; - - inspect?: Maybe; -} - -export interface UsersEdges { - node: UsersNode; - - cursor: CursorType; -} - -export interface UsersNode { - _id?: Maybe; - - timestamp?: Maybe; - - user?: Maybe; -} - -export interface UsersItem { - name?: Maybe; - - id?: Maybe; - - groupId?: Maybe; - - groupName?: Maybe; - - count?: Maybe; -} - export interface KpiNetworkData { networkEvents?: Maybe; @@ -2282,34 +2197,6 @@ export interface HostFirstLastSeenSourceArgs { docValueFields: DocValueFieldsInput[]; } -export interface IpOverviewSourceArgs { - id?: Maybe; - - filterQuery?: Maybe; - - ip: string; - - defaultIndex: string[]; - - docValueFields: DocValueFieldsInput[]; -} -export interface UsersSourceArgs { - filterQuery?: Maybe; - - id?: Maybe; - - ip: string; - - pagination: PaginationInputPaginated; - - sort: UsersSortField; - - flowTarget: FlowTarget; - - timerange: TimerangeInput; - - defaultIndex: string[]; -} export interface KpiNetworkSourceArgs { id?: Maybe; @@ -3071,185 +2958,6 @@ export namespace GetKpiHostsQuery { }; } -export namespace GetIpOverviewQuery { - export type Variables = { - sourceId: string; - filterQuery?: Maybe; - ip: string; - defaultIndex: string[]; - inspect: boolean; - docValueFields: DocValueFieldsInput[]; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - IpOverview: Maybe; - }; - - export type IpOverview = { - __typename?: 'IpOverviewData'; - - source: Maybe<_Source>; - - destination: Maybe; - - host: Host; - - inspect: Maybe; - }; - - export type _Source = { - __typename?: 'Overview'; - - firstSeen: Maybe; - - lastSeen: Maybe; - - autonomousSystem: AutonomousSystem; - - geo: Geo; - }; - - export type AutonomousSystem = { - __typename?: 'AutonomousSystem'; - - number: Maybe; - - organization: Maybe; - }; - - export type Organization = { - __typename?: 'AutonomousSystemOrganization'; - - name: Maybe; - }; - - export type Geo = { - __typename?: 'GeoEcsFields'; - - continent_name: Maybe; - - city_name: Maybe; - - country_iso_code: Maybe; - - country_name: Maybe; - - location: Maybe; - - region_iso_code: Maybe; - - region_name: Maybe; - }; - - export type Location = { - __typename?: 'Location'; - - lat: Maybe; - - lon: Maybe; - }; - - export type Destination = { - __typename?: 'Overview'; - - firstSeen: Maybe; - - lastSeen: Maybe; - - autonomousSystem: _AutonomousSystem; - - geo: _Geo; - }; - - export type _AutonomousSystem = { - __typename?: 'AutonomousSystem'; - - number: Maybe; - - organization: Maybe<_Organization>; - }; - - export type _Organization = { - __typename?: 'AutonomousSystemOrganization'; - - name: Maybe; - }; - - export type _Geo = { - __typename?: 'GeoEcsFields'; - - continent_name: Maybe; - - city_name: Maybe; - - country_iso_code: Maybe; - - country_name: Maybe; - - location: Maybe<_Location>; - - region_iso_code: Maybe; - - region_name: Maybe; - }; - - export type _Location = { - __typename?: 'Location'; - - lat: Maybe; - - lon: Maybe; - }; - - export type Host = { - __typename?: 'HostEcsFields'; - - architecture: Maybe; - - id: Maybe; - - ip: Maybe; - - mac: Maybe; - - name: Maybe; - - os: Maybe; - - type: Maybe; - }; - - export type Os = { - __typename?: 'OsEcsFields'; - - family: Maybe; - - name: Maybe; - - platform: Maybe; - - version: Maybe; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - export namespace GetKpiNetworkQuery { export type Variables = { sourceId: string; @@ -3785,98 +3493,6 @@ export namespace GetNetworkTopNFlowQuery { }; } -export namespace GetUsersQuery { - export type Variables = { - sourceId: string; - filterQuery?: Maybe; - flowTarget: FlowTarget; - ip: string; - pagination: PaginationInputPaginated; - sort: UsersSortField; - timerange: TimerangeInput; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - Users: Users; - }; - - export type Users = { - __typename?: 'UsersData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'UsersEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'UsersNode'; - - user: Maybe; - }; - - export type User = { - __typename?: 'UsersItem'; - - name: Maybe; - - id: Maybe; - - groupId: Maybe; - - groupName: Maybe; - - count: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - export namespace GetAllTimeline { export type Variables = { pageInfo: PageInfoTimeline; diff --git a/x-pack/plugins/security_solution/public/network/components/users_table/columns.tsx b/x-pack/plugins/security_solution/public/network/components/users_table/columns.tsx index b7f78873423351..afef7fe7949393 100644 --- a/x-pack/plugins/security_solution/public/network/components/users_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/network/components/users_table/columns.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FlowTarget, UsersItem } from '../../../graphql/types'; +import { FlowTarget, NetworkUsersItem } from '../../../../common/search_strategy'; import { defaultToEmptyTag } from '../../../common/components/empty_value'; import { Columns } from '../../../common/components/paginated_table'; @@ -15,11 +15,11 @@ import { } from '../../../common/components/tables/helpers'; export type UsersColumns = [ - Columns, - Columns, - Columns, - Columns, - Columns + Columns, + Columns, + Columns, + Columns, + Columns ]; export const getUsersColumns = (flowTarget: FlowTarget, tableId: string): UsersColumns => [ diff --git a/x-pack/plugins/security_solution/public/network/components/users_table/mock.ts b/x-pack/plugins/security_solution/public/network/components/users_table/mock.ts index 50bef1867aa3b4..9180ee328f988f 100644 --- a/x-pack/plugins/security_solution/public/network/components/users_table/mock.ts +++ b/x-pack/plugins/security_solution/public/network/components/users_table/mock.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UsersData } from '../../../graphql/types'; +import { NetworkUsersStrategyResponse } from '../../../../common/search_strategy'; -export const mockUsersData: UsersData = { +export const mockUsersData: NetworkUsersStrategyResponse = { edges: [ { node: { @@ -63,4 +63,5 @@ export const mockUsersData: UsersData = { fakeTotalCount: 3, showMorePagesIndicator: true, }, + rawResponse: {} as NetworkUsersStrategyResponse['rawResponse'], }; diff --git a/x-pack/plugins/security_solution/public/network/containers/details/index.gql_query.ts b/x-pack/plugins/security_solution/public/network/containers/details/index.gql_query.ts deleted file mode 100644 index 6ebb60ccb4ea6b..00000000000000 --- a/x-pack/plugins/security_solution/public/network/containers/details/index.gql_query.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -export const ipOverviewQuery = gql` - query GetIpOverviewQuery( - $sourceId: ID! - $filterQuery: String - $ip: String! - $defaultIndex: [String!]! - $inspect: Boolean! - $docValueFields: [docValueFieldsInput!]! - ) { - source(id: $sourceId) { - id - IpOverview( - filterQuery: $filterQuery - ip: $ip - defaultIndex: $defaultIndex - docValueFields: $docValueFields - ) { - source { - firstSeen - lastSeen - autonomousSystem { - number - organization { - name - } - } - geo { - continent_name - city_name - country_iso_code - country_name - location { - lat - lon - } - region_iso_code - region_name - } - } - destination { - firstSeen - lastSeen - autonomousSystem { - number - organization { - name - } - } - geo { - continent_name - city_name - country_iso_code - country_name - location { - lat - lon - } - region_iso_code - region_name - } - } - host { - architecture - id - ip - mac - name - os { - family - name - platform - version - } - type - } - inspect @include(if: $inspect) { - dsl - response - } - } - } - } -`; diff --git a/x-pack/plugins/security_solution/public/network/containers/users/index.gql_query.ts b/x-pack/plugins/security_solution/public/network/containers/users/index.gql_query.ts deleted file mode 100644 index 3fc1cdfd160dba..00000000000000 --- a/x-pack/plugins/security_solution/public/network/containers/users/index.gql_query.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -export const usersQuery = gql` - query GetUsersQuery( - $sourceId: ID! - $filterQuery: String - $flowTarget: FlowTarget! - $ip: String! - $pagination: PaginationInputPaginated! - $sort: UsersSortField! - $timerange: TimerangeInput! - $defaultIndex: [String!]! - $inspect: Boolean! - ) { - source(id: $sourceId) { - id - Users( - filterQuery: $filterQuery - flowTarget: $flowTarget - ip: $ip - pagination: $pagination - sort: $sort - timerange: $timerange - defaultIndex: $defaultIndex - ) { - totalCount - edges { - node { - user { - name - id - groupId - groupName - count - } - } - cursor { - value - } - } - pageInfo { - activePage - fakeTotalCount - showMorePagesIndicator - } - inspect @include(if: $inspect) { - dsl - response - } - } - } - } -`; diff --git a/x-pack/plugins/security_solution/server/graphql/index.ts b/x-pack/plugins/security_solution/server/graphql/index.ts index 2de6ef32b57031..d23494e0eeaa65 100644 --- a/x-pack/plugins/security_solution/server/graphql/index.ts +++ b/x-pack/plugins/security_solution/server/graphql/index.ts @@ -11,7 +11,6 @@ import { authenticationsSchema } from './authentications'; import { ecsSchema } from './ecs'; import { eventsSchema } from './events'; import { hostsSchema } from './hosts'; -import { ipDetailsSchemas } from './ip_details'; import { kpiHostsSchema } from './kpi_hosts'; import { kpiNetworkSchema } from './kpi_network'; import { networkSchema } from './network'; @@ -37,7 +36,6 @@ export const schemas = [ toDateSchema, toBooleanSchema, hostsSchema, - ...ipDetailsSchemas, kpiNetworkSchema, kpiHostsSchema, matrixHistogramSchema, diff --git a/x-pack/plugins/security_solution/server/graphql/ip_details/index.ts b/x-pack/plugins/security_solution/server/graphql/ip_details/index.ts deleted file mode 100644 index 186397ea347cb4..00000000000000 --- a/x-pack/plugins/security_solution/server/graphql/ip_details/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { createIpDetailsResolvers } from './resolvers'; -export { ipDetailsSchemas } from './schema.gql'; diff --git a/x-pack/plugins/security_solution/server/graphql/ip_details/resolvers.ts b/x-pack/plugins/security_solution/server/graphql/ip_details/resolvers.ts deleted file mode 100644 index d0e84026de473f..00000000000000 --- a/x-pack/plugins/security_solution/server/graphql/ip_details/resolvers.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { SourceResolvers } from '../../graphql/types'; -import { AppResolverOf, ChildResolverOf } from '../../lib/framework'; -import { IpDetails, UsersRequestOptions } from '../../lib/ip_details'; -import { createOptions, createOptionsPaginated } from '../../utils/build_query/create_options'; -import { QuerySourceResolver } from '../sources/resolvers'; - -export type QueryIpOverviewResolver = ChildResolverOf< - AppResolverOf, - QuerySourceResolver ->; - -export type QueryUsersResolver = ChildResolverOf< - AppResolverOf, - QuerySourceResolver ->; - -export interface IDetailsResolversDeps { - ipDetails: IpDetails; -} - -export const createIpDetailsResolvers = ( - libs: IDetailsResolversDeps -): { - Source: { - IpOverview: QueryIpOverviewResolver; - Users: QueryUsersResolver; - }; -} => ({ - Source: { - async IpOverview(source, args, { req }, info) { - const options = { ...createOptions(source, args, info), ip: args.ip }; - return libs.ipDetails.getIpOverview(req, options); - }, - async Users(source, args, { req }, info) { - const options: UsersRequestOptions = { - ...createOptionsPaginated(source, args, info), - ip: args.ip, - sort: args.sort, - flowTarget: args.flowTarget, - }; - return libs.ipDetails.getUsers(req, options); - }, - }, -}); diff --git a/x-pack/plugins/security_solution/server/graphql/ip_details/schema.gql.ts b/x-pack/plugins/security_solution/server/graphql/ip_details/schema.gql.ts deleted file mode 100644 index 2531f8d169327b..00000000000000 --- a/x-pack/plugins/security_solution/server/graphql/ip_details/schema.gql.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import gql from 'graphql-tag'; - -const ipOverviewSchema = gql` - type AutonomousSystemOrganization { - name: String - } - - type AutonomousSystem { - number: Float - organization: AutonomousSystemOrganization - } - - type Overview { - firstSeen: Date - lastSeen: Date - autonomousSystem: AutonomousSystem! - geo: GeoEcsFields! - } - - type IpOverviewData { - client: Overview - destination: Overview - host: HostEcsFields! - server: Overview - source: Overview - inspect: Inspect - } - - extend type Source { - IpOverview( - id: String - filterQuery: String - ip: String! - defaultIndex: [String!]! - docValueFields: [docValueFieldsInput!]! - ): IpOverviewData - } -`; - -const usersSchema = gql` - enum UsersFields { - name - count - } - - input UsersSortField { - field: UsersFields! - direction: Direction! - } - - type UsersItem { - name: String - id: ToStringArray - groupId: ToStringArray - groupName: ToStringArray - count: Float - } - - type UsersNode { - _id: String - timestamp: Date - user: UsersItem - } - - type UsersEdges { - node: UsersNode! - cursor: CursorType! - } - - type UsersData { - edges: [UsersEdges!]! - totalCount: Float! - pageInfo: PageInfoPaginated! - inspect: Inspect - } - - extend type Source { - Users( - filterQuery: String - id: String - ip: String! - pagination: PaginationInputPaginated! - sort: UsersSortField! - flowTarget: FlowTarget! - timerange: TimerangeInput! - defaultIndex: [String!]! - ): UsersData! - } -`; - -export const ipDetailsSchemas = [ipOverviewSchema, usersSchema]; diff --git a/x-pack/plugins/security_solution/server/graphql/types.ts b/x-pack/plugins/security_solution/server/graphql/types.ts index d10dfb16a9b8ac..5f370ab1b8c9f7 100644 --- a/x-pack/plugins/security_solution/server/graphql/types.ts +++ b/x-pack/plugins/security_solution/server/graphql/types.ts @@ -75,12 +75,6 @@ export interface HostsSortField { direction: Direction; } -export interface UsersSortField { - field: UsersFields; - - direction: Direction; -} - export interface NetworkTopTablesSortField { field: NetworkTopTablesFields; @@ -311,18 +305,6 @@ export enum HostPolicyResponseActionStatus { warning = 'warning', } -export enum UsersFields { - name = 'name', - count = 'count', -} - -export enum FlowTarget { - client = 'client', - destination = 'destination', - server = 'server', - source = 'source', -} - export enum HistogramType { authentications = 'authentications', anomalies = 'anomalies', @@ -412,6 +394,13 @@ export enum NetworkHttpFields { statuses = 'statuses', } +export enum FlowTarget { + client = 'client', + destination = 'destination', + server = 'server', + source = 'source', +} + export enum FlowDirection { uniDirectional = 'uniDirectional', biDirectional = 'biDirectional', @@ -537,10 +526,6 @@ export interface Source { HostFirstLastSeen: FirstLastSeenHost; - IpOverview?: Maybe; - - Users: UsersData; - KpiNetwork?: Maybe; KpiHosts: KpiHostsData; @@ -1464,76 +1449,6 @@ export interface FirstLastSeenHost { lastSeen?: Maybe; } -export interface IpOverviewData { - client?: Maybe; - - destination?: Maybe; - - host: HostEcsFields; - - server?: Maybe; - - source?: Maybe; - - inspect?: Maybe; -} - -export interface Overview { - firstSeen?: Maybe; - - lastSeen?: Maybe; - - autonomousSystem: AutonomousSystem; - - geo: GeoEcsFields; -} - -export interface AutonomousSystem { - number?: Maybe; - - organization?: Maybe; -} - -export interface AutonomousSystemOrganization { - name?: Maybe; -} - -export interface UsersData { - edges: UsersEdges[]; - - totalCount: number; - - pageInfo: PageInfoPaginated; - - inspect?: Maybe; -} - -export interface UsersEdges { - node: UsersNode; - - cursor: CursorType; -} - -export interface UsersNode { - _id?: Maybe; - - timestamp?: Maybe; - - user?: Maybe; -} - -export interface UsersItem { - name?: Maybe; - - id?: Maybe; - - groupId?: Maybe; - - groupName?: Maybe; - - count?: Maybe; -} - export interface KpiNetworkData { networkEvents?: Maybe; @@ -2284,34 +2199,6 @@ export interface HostFirstLastSeenSourceArgs { docValueFields: DocValueFieldsInput[]; } -export interface IpOverviewSourceArgs { - id?: Maybe; - - filterQuery?: Maybe; - - ip: string; - - defaultIndex: string[]; - - docValueFields: DocValueFieldsInput[]; -} -export interface UsersSourceArgs { - filterQuery?: Maybe; - - id?: Maybe; - - ip: string; - - pagination: PaginationInputPaginated; - - sort: UsersSortField; - - flowTarget: FlowTarget; - - timerange: TimerangeInput; - - defaultIndex: string[]; -} export interface KpiNetworkSourceArgs { id?: Maybe; @@ -2838,10 +2725,6 @@ export namespace SourceResolvers { HostFirstLastSeen?: HostFirstLastSeenResolver; - IpOverview?: IpOverviewResolver, TypeParent, TContext>; - - Users?: UsersResolver; - KpiNetwork?: KpiNetworkResolver, TypeParent, TContext>; KpiHosts?: KpiHostsResolver; @@ -3004,47 +2887,6 @@ export namespace SourceResolvers { docValueFields: DocValueFieldsInput[]; } - export type IpOverviewResolver< - R = Maybe, - Parent = Source, - TContext = SiemContext - > = Resolver; - export interface IpOverviewArgs { - id?: Maybe; - - filterQuery?: Maybe; - - ip: string; - - defaultIndex: string[]; - - docValueFields: DocValueFieldsInput[]; - } - - export type UsersResolver = Resolver< - R, - Parent, - TContext, - UsersArgs - >; - export interface UsersArgs { - filterQuery?: Maybe; - - id?: Maybe; - - ip: string; - - pagination: PaginationInputPaginated; - - sort: UsersSortField; - - flowTarget: FlowTarget; - - timerange: TimerangeInput; - - defaultIndex: string[]; - } - export type KpiNetworkResolver< R = Maybe, Parent = Source, @@ -6223,235 +6065,6 @@ export namespace FirstLastSeenHostResolvers { > = Resolver; } -export namespace IpOverviewDataResolvers { - export interface Resolvers { - client?: ClientResolver, TypeParent, TContext>; - - destination?: DestinationResolver, TypeParent, TContext>; - - host?: HostResolver; - - server?: ServerResolver, TypeParent, TContext>; - - source?: SourceResolver, TypeParent, TContext>; - - inspect?: InspectResolver, TypeParent, TContext>; - } - - export type ClientResolver< - R = Maybe, - Parent = IpOverviewData, - TContext = SiemContext - > = Resolver; - export type DestinationResolver< - R = Maybe, - Parent = IpOverviewData, - TContext = SiemContext - > = Resolver; - export type HostResolver< - R = HostEcsFields, - Parent = IpOverviewData, - TContext = SiemContext - > = Resolver; - export type ServerResolver< - R = Maybe, - Parent = IpOverviewData, - TContext = SiemContext - > = Resolver; - export type SourceResolver< - R = Maybe, - Parent = IpOverviewData, - TContext = SiemContext - > = Resolver; - export type InspectResolver< - R = Maybe, - Parent = IpOverviewData, - TContext = SiemContext - > = Resolver; -} - -export namespace OverviewResolvers { - export interface Resolvers { - firstSeen?: FirstSeenResolver, TypeParent, TContext>; - - lastSeen?: LastSeenResolver, TypeParent, TContext>; - - autonomousSystem?: AutonomousSystemResolver; - - geo?: GeoResolver; - } - - export type FirstSeenResolver< - R = Maybe, - Parent = Overview, - TContext = SiemContext - > = Resolver; - export type LastSeenResolver< - R = Maybe, - Parent = Overview, - TContext = SiemContext - > = Resolver; - export type AutonomousSystemResolver< - R = AutonomousSystem, - Parent = Overview, - TContext = SiemContext - > = Resolver; - export type GeoResolver = Resolver< - R, - Parent, - TContext - >; -} - -export namespace AutonomousSystemResolvers { - export interface Resolvers { - number?: NumberResolver, TypeParent, TContext>; - - organization?: OrganizationResolver, TypeParent, TContext>; - } - - export type NumberResolver< - R = Maybe, - Parent = AutonomousSystem, - TContext = SiemContext - > = Resolver; - export type OrganizationResolver< - R = Maybe, - Parent = AutonomousSystem, - TContext = SiemContext - > = Resolver; -} - -export namespace AutonomousSystemOrganizationResolvers { - export interface Resolvers { - name?: NameResolver, TypeParent, TContext>; - } - - export type NameResolver< - R = Maybe, - Parent = AutonomousSystemOrganization, - TContext = SiemContext - > = Resolver; -} - -export namespace UsersDataResolvers { - export interface Resolvers { - edges?: EdgesResolver; - - totalCount?: TotalCountResolver; - - pageInfo?: PageInfoResolver; - - inspect?: InspectResolver, TypeParent, TContext>; - } - - export type EdgesResolver< - R = UsersEdges[], - Parent = UsersData, - TContext = SiemContext - > = Resolver; - export type TotalCountResolver = Resolver< - R, - Parent, - TContext - >; - export type PageInfoResolver< - R = PageInfoPaginated, - Parent = UsersData, - TContext = SiemContext - > = Resolver; - export type InspectResolver< - R = Maybe, - Parent = UsersData, - TContext = SiemContext - > = Resolver; -} - -export namespace UsersEdgesResolvers { - export interface Resolvers { - node?: NodeResolver; - - cursor?: CursorResolver; - } - - export type NodeResolver = Resolver< - R, - Parent, - TContext - >; - export type CursorResolver< - R = CursorType, - Parent = UsersEdges, - TContext = SiemContext - > = Resolver; -} - -export namespace UsersNodeResolvers { - export interface Resolvers { - _id?: _IdResolver, TypeParent, TContext>; - - timestamp?: TimestampResolver, TypeParent, TContext>; - - user?: UserResolver, TypeParent, TContext>; - } - - export type _IdResolver, Parent = UsersNode, TContext = SiemContext> = Resolver< - R, - Parent, - TContext - >; - export type TimestampResolver< - R = Maybe, - Parent = UsersNode, - TContext = SiemContext - > = Resolver; - export type UserResolver< - R = Maybe, - Parent = UsersNode, - TContext = SiemContext - > = Resolver; -} - -export namespace UsersItemResolvers { - export interface Resolvers { - name?: NameResolver, TypeParent, TContext>; - - id?: IdResolver, TypeParent, TContext>; - - groupId?: GroupIdResolver, TypeParent, TContext>; - - groupName?: GroupNameResolver, TypeParent, TContext>; - - count?: CountResolver, TypeParent, TContext>; - } - - export type NameResolver< - R = Maybe, - Parent = UsersItem, - TContext = SiemContext - > = Resolver; - export type IdResolver< - R = Maybe, - Parent = UsersItem, - TContext = SiemContext - > = Resolver; - export type GroupIdResolver< - R = Maybe, - Parent = UsersItem, - TContext = SiemContext - > = Resolver; - export type GroupNameResolver< - R = Maybe, - Parent = UsersItem, - TContext = SiemContext - > = Resolver; - export type CountResolver< - R = Maybe, - Parent = UsersItem, - TContext = SiemContext - > = Resolver; -} - export namespace KpiNetworkDataResolvers { export interface Resolvers { networkEvents?: NetworkEventsResolver, TypeParent, TContext>; @@ -8815,14 +8428,6 @@ export type IResolvers = { CloudMachine?: CloudMachineResolvers.Resolvers; EndpointFields?: EndpointFieldsResolvers.Resolvers; FirstLastSeenHost?: FirstLastSeenHostResolvers.Resolvers; - IpOverviewData?: IpOverviewDataResolvers.Resolvers; - Overview?: OverviewResolvers.Resolvers; - AutonomousSystem?: AutonomousSystemResolvers.Resolvers; - AutonomousSystemOrganization?: AutonomousSystemOrganizationResolvers.Resolvers; - UsersData?: UsersDataResolvers.Resolvers; - UsersEdges?: UsersEdgesResolvers.Resolvers; - UsersNode?: UsersNodeResolvers.Resolvers; - UsersItem?: UsersItemResolvers.Resolvers; KpiNetworkData?: KpiNetworkDataResolvers.Resolvers; KpiNetworkHistogramData?: KpiNetworkHistogramDataResolvers.Resolvers; KpiHostsData?: KpiHostsDataResolvers.Resolvers; diff --git a/x-pack/plugins/security_solution/server/init_server.ts b/x-pack/plugins/security_solution/server/init_server.ts index ac0273ec1770de..3d2833f1c6c607 100644 --- a/x-pack/plugins/security_solution/server/init_server.ts +++ b/x-pack/plugins/security_solution/server/init_server.ts @@ -10,7 +10,6 @@ import { createAuthenticationsResolvers } from './graphql/authentications'; import { createScalarToStringArrayValueResolvers } from './graphql/ecs'; import { createEsValueResolvers, createEventsResolvers } from './graphql/events'; import { createHostsResolvers } from './graphql/hosts'; -import { createIpDetailsResolvers } from './graphql/ip_details'; import { createKpiHostsResolvers } from './graphql/kpi_hosts'; import { createKpiNetworkResolvers } from './graphql/kpi_network'; import { createNetworkResolvers } from './graphql/network'; @@ -35,7 +34,6 @@ export const initServer = (libs: AppBackendLibs) => { createEsValueResolvers() as IResolvers, createEventsResolvers(libs) as IResolvers, createHostsResolvers(libs) as IResolvers, - createIpDetailsResolvers(libs) as IResolvers, createKpiNetworkResolvers(libs) as IResolvers, createMatrixHistogramResolvers(libs) as IResolvers, createNoteResolvers(libs) as IResolvers, diff --git a/x-pack/plugins/security_solution/server/lib/compose/kibana.ts b/x-pack/plugins/security_solution/server/lib/compose/kibana.ts index 3bfb3d9492353e..6348ee930a1090 100644 --- a/x-pack/plugins/security_solution/server/lib/compose/kibana.ts +++ b/x-pack/plugins/security_solution/server/lib/compose/kibana.ts @@ -16,7 +16,6 @@ import { KpiHosts } from '../kpi_hosts'; import { ElasticsearchKpiHostsAdapter } from '../kpi_hosts/elasticsearch_adapter'; import { ElasticsearchIndexFieldAdapter, IndexFields } from '../index_fields'; -import { ElasticsearchIpDetailsAdapter, IpDetails } from '../ip_details'; import { KpiNetwork } from '../kpi_network'; import { ElasticsearchKpiNetworkAdapter } from '../kpi_network/elasticsearch_adapter'; @@ -45,7 +44,6 @@ export function compose( events: new Events(new ElasticsearchEventsAdapter(framework)), fields: new IndexFields(new ElasticsearchIndexFieldAdapter()), hosts: new Hosts(new ElasticsearchHostsAdapter(framework, endpointContext)), - ipDetails: new IpDetails(new ElasticsearchIpDetailsAdapter(framework)), kpiHosts: new KpiHosts(new ElasticsearchKpiHostsAdapter(framework)), kpiNetwork: new KpiNetwork(new ElasticsearchKpiNetworkAdapter(framework)), matrixHistogram: new MatrixHistogram(new ElasticsearchMatrixHistogramAdapter(framework)), diff --git a/x-pack/plugins/security_solution/server/lib/ip_details/elasticsearch_adapter.test.ts b/x-pack/plugins/security_solution/server/lib/ip_details/elasticsearch_adapter.test.ts deleted file mode 100644 index 6249e60d9a2be8..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/ip_details/elasticsearch_adapter.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { FlowTarget } from '../../graphql/types'; - -import { getIpOverviewAgg, getIpOverviewHostAgg, getUsersEdges } from './elasticsearch_adapter'; - -import { - formattedDestination, - formattedEmptySource, - formattedHost, - formattedSource, - mockFormattedUsersEdges, - mockUsersData, - responseAggs, -} from './mock'; - -describe('elasticsearch_adapter', () => { - describe('#getIpOverview', () => { - test('will return a destination correctly', () => { - const destination = getIpOverviewAgg( - FlowTarget.destination, - responseAggs.aggregations.destination! - ); - expect(destination).toEqual(formattedDestination); - }); - - test('will return a source correctly', () => { - const source = getIpOverviewAgg(FlowTarget.source, responseAggs.aggregations.source!); - expect(source).toEqual(formattedSource); - }); - - test('will return a host correctly', () => { - const host = getIpOverviewHostAgg(responseAggs.aggregations.host); - expect(host).toEqual(formattedHost); - }); - - test('will return an empty source correctly', () => { - const source = getIpOverviewAgg(FlowTarget.source, {}); - expect(source).toEqual(formattedEmptySource); - }); - }); - - describe('#getUsers', () => { - test('will format edges correctly', () => { - // @ts-expect-error Re-work `DatabaseSearchResponse` types as mock ES Response won't match - const edges = getUsersEdges(mockUsersData); - expect(edges).toEqual(mockFormattedUsersEdges); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/ip_details/elasticsearch_adapter.ts b/x-pack/plugins/security_solution/server/lib/ip_details/elasticsearch_adapter.ts deleted file mode 100644 index 90803ca302bd4f..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/ip_details/elasticsearch_adapter.ts +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { get, getOr } from 'lodash/fp'; - -import { - AutonomousSystem, - GeoEcsFields, - HostEcsFields, - IpOverviewData, - UsersData, - UsersEdges, -} from '../../graphql/types'; -import { inspectStringifyObject } from '../../utils/build_query'; -import { DatabaseSearchResponse, FrameworkAdapter, FrameworkRequest } from '../framework'; -import { TermAggregation } from '../types'; -import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../common/constants'; -import { IpOverviewRequestOptions, UsersRequestOptions } from './index'; -import { buildOverviewQuery } from './query_overview.dsl'; -import { buildUsersQuery } from './query_users.dsl'; - -import { - IpDetailsAdapter, - IpOverviewHit, - OverviewHit, - OverviewHostHit, - UsersBucketsItem, -} from './types'; - -export class ElasticsearchIpDetailsAdapter implements IpDetailsAdapter { - constructor(private readonly framework: FrameworkAdapter) {} - - public async getIpDetails( - request: FrameworkRequest, - options: IpOverviewRequestOptions - ): Promise { - const dsl = buildOverviewQuery(options); - const response = await this.framework.callWithRequest( - request, - 'search', - dsl - ); - - const inspect = { - dsl: [inspectStringifyObject(dsl)], - response: [inspectStringifyObject(response)], - }; - - return { - inspect, - ...getIpOverviewAgg('source', getOr({}, 'aggregations.source', response)), - ...getIpOverviewAgg('destination', getOr({}, 'aggregations.destination', response)), - ...getIpOverviewHostAgg(getOr({}, 'aggregations.host', response)), - }; - } - - public async getUsers( - request: FrameworkRequest, - options: UsersRequestOptions - ): Promise { - if (options.pagination && options.pagination.querySize >= DEFAULT_MAX_TABLE_QUERY_SIZE) { - throw new Error(`No query size above ${DEFAULT_MAX_TABLE_QUERY_SIZE}`); - } - const dsl = buildUsersQuery(options); - const response = await this.framework.callWithRequest( - request, - 'search', - dsl - ); - - const { activePage, cursorStart, fakePossibleCount, querySize } = options.pagination; - const totalCount = getOr(0, 'aggregations.user_count.value', response); - const usersEdges = getUsersEdges(response); - const fakeTotalCount = fakePossibleCount <= totalCount ? fakePossibleCount : totalCount; - const edges = usersEdges.splice(cursorStart, querySize - cursorStart); - const inspect = { - dsl: [inspectStringifyObject(dsl)], - response: [inspectStringifyObject(response)], - }; - const showMorePagesIndicator = totalCount > fakeTotalCount; - return { - edges, - inspect, - pageInfo: { - activePage: activePage ? activePage : 0, - fakeTotalCount, - showMorePagesIndicator, - }, - totalCount, - }; - } -} - -export const getIpOverviewAgg = (type: string, overviewHit: OverviewHit | {}) => { - const firstSeen = getOr(null, `firstSeen.value_as_string`, overviewHit); - const lastSeen = getOr(null, `lastSeen.value_as_string`, overviewHit); - const autonomousSystem: AutonomousSystem | null = getOr( - null, - `as.results.hits.hits[0]._source.${type}.as`, - overviewHit - ); - const geoFields: GeoEcsFields | null = getOr( - null, - `geo.results.hits.hits[0]._source.${type}.geo`, - overviewHit - ); - - return { - [type]: { - firstSeen, - lastSeen, - autonomousSystem: { - ...autonomousSystem, - }, - geo: { - ...geoFields, - }, - }, - }; -}; - -export const getIpOverviewHostAgg = (overviewHostHit: OverviewHostHit | {}) => { - const hostFields: HostEcsFields | null = getOr( - null, - `results.hits.hits[0]._source.host`, - overviewHostHit - ); - return { - host: { - ...hostFields, - }, - }; -}; - -export const getUsersEdges = ( - response: DatabaseSearchResponse -): UsersEdges[] => - getOr([], `aggregations.users.buckets`, response).map((bucket: UsersBucketsItem) => ({ - node: { - _id: bucket.key, - user: { - id: getOr([], 'id.buckets', bucket).map((id: UsersBucketsItem) => id.key), - name: bucket.key, - groupId: getOr([], 'groupId.buckets', bucket).map( - (groupId: UsersBucketsItem) => groupId.key - ), - groupName: getOr([], 'groupName.buckets', bucket).map( - (groupName: UsersBucketsItem) => groupName.key - ), - count: get('doc_count', bucket), - }, - }, - cursor: { - value: bucket.key, - tiebreaker: null, - }, - })); diff --git a/x-pack/plugins/security_solution/server/lib/ip_details/index.ts b/x-pack/plugins/security_solution/server/lib/ip_details/index.ts deleted file mode 100644 index ed8824bc284e4b..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/ip_details/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { FlowTarget, IpOverviewData, UsersData, UsersSortField } from '../../graphql/types'; -import { FrameworkRequest, RequestOptions, RequestOptionsPaginated } from '../framework'; - -import { IpDetailsAdapter } from './types'; - -export * from './elasticsearch_adapter'; - -export interface IpOverviewRequestOptions extends RequestOptions { - ip: string; -} - -export interface UsersRequestOptions extends RequestOptionsPaginated { - ip: string; - sort: UsersSortField; - flowTarget: FlowTarget; -} - -export class IpDetails { - constructor(private readonly adapter: IpDetailsAdapter) {} - - public async getIpOverview( - req: FrameworkRequest, - options: IpOverviewRequestOptions - ): Promise { - return this.adapter.getIpDetails(req, options); - } - - public async getUsers(req: FrameworkRequest, options: UsersRequestOptions): Promise { - return this.adapter.getUsers(req, options); - } -} diff --git a/x-pack/plugins/security_solution/server/lib/ip_details/mock.ts b/x-pack/plugins/security_solution/server/lib/ip_details/mock.ts deleted file mode 100644 index 1db86e7766fcf0..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/ip_details/mock.ts +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { UsersEdges } from '../../graphql/types'; - -import { IpOverviewHit, UsersResponse } from './types'; - -export const responseAggs: IpOverviewHit = { - aggregations: { - destination: { - doc_count: 882307, - geo: { - doc_count: 62089, - results: { - hits: { - total: { - value: 62089, - relation: 'eq', - }, - max_score: null, - hits: [ - { - _source: { - destination: { - geo: { - continent_name: 'Asia', - region_iso_code: 'IN-KA', - city_name: 'Bengaluru', - country_iso_code: 'IN', - region_name: 'Karnataka', - location: { - lon: 77.5833, - lat: 12.9833, - }, - }, - }, - }, - sort: [1553894176003], - }, - ], - }, - }, - }, - lastSeen: { - value: 1553900180003, - value_as_string: '2019-03-29T22:56:20.003Z', - }, - firstSeen: { - value: 1551388820000, - value_as_string: '2019-02-28T21:20:20.000Z', - }, - autonomousSystem: { - doc_count: 0, - results: { - hits: { - total: { - value: 0, - relation: 'eq', - }, - max_score: null, - hits: [], - }, - }, - }, - }, - source: { - doc_count: 1002234, - geo: { - doc_count: 1507, - results: { - hits: { - total: { - value: 1507, - relation: 'eq', - }, - max_score: null, - hits: [ - { - _index: 'filebeat-8.0.0-2019.03.21-000002', - _type: '_doc', - _id: 'dHQ6y2kBCQofM5eXi5OE', - _score: null, - _source: { - source: { - geo: { - continent_name: 'Asia', - region_iso_code: 'IN-KA', - city_name: 'Bengaluru', - country_iso_code: 'IN', - region_name: 'Karnataka', - location: { - lon: 77.5833, - lat: 12.9833, - }, - }, - }, - }, - sort: [1553892804003], - }, - ], - }, - }, - }, - lastSeen: { - value: 1553900180003, - value_as_string: '2019-03-29T22:56:20.003Z', - }, - firstSeen: { - value: 1551388804322, - value_as_string: '2019-02-28T21:20:04.322Z', - }, - autonomousSystem: { - doc_count: 0, - results: { - hits: { - total: { - value: 0, - relation: 'eq', - }, - max_score: null, - hits: [], - }, - }, - }, - }, - host: { - doc_count: 1588091, - results: { - hits: { - total: { - value: 1588091, - relation: 'eq', - }, - max_score: null, - hits: [ - { - _index: 'filebeat-8.0.0-2019.05.20-000004', - _type: '_doc', - _id: 'NU9dD2sB9v5HJNSHMMRc', - _score: null, - _source: { - host: { - hostname: 'suricata-iowa', - os: { - kernel: '4.15.0-1032-gcp', - codename: 'bionic', - name: 'Ubuntu', - family: 'debian', - version: '18.04.2 LTS (Bionic Beaver)', - platform: 'ubuntu', - }, - ip: ['10.128.0.4', 'fe80::4001:aff:fe80:4'], - containerized: false, - name: 'suricata-iowa', - id: 'be1f3d767896212736b880e846876dcb', - mac: ['42:01:0a:80:00:04'], - architecture: 'x86_64', - }, - }, - sort: [1559330892000], - }, - ], - }, - }, - }, - }, - _shards: { - total: 42, - successful: 42, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 71358841, - relation: 'eq', - }, - max_score: null, - hits: [], - }, - took: 392, - timeout: 500, -}; - -export const formattedDestination = { - destination: { - firstSeen: '2019-02-28T21:20:20.000Z', - lastSeen: '2019-03-29T22:56:20.003Z', - autonomousSystem: {}, - geo: { - continent_name: 'Asia', - region_iso_code: 'IN-KA', - city_name: 'Bengaluru', - country_iso_code: 'IN', - region_name: 'Karnataka', - location: { - lon: 77.5833, - lat: 12.9833, - }, - }, - }, -}; - -export const formattedSource = { - source: { - firstSeen: '2019-02-28T21:20:04.322Z', - lastSeen: '2019-03-29T22:56:20.003Z', - autonomousSystem: {}, - geo: { - continent_name: 'Asia', - region_iso_code: 'IN-KA', - city_name: 'Bengaluru', - country_iso_code: 'IN', - region_name: 'Karnataka', - location: { - lon: 77.5833, - lat: 12.9833, - }, - }, - }, -}; - -export const formattedHost = { - host: { - hostname: 'suricata-iowa', - os: { - kernel: '4.15.0-1032-gcp', - codename: 'bionic', - name: 'Ubuntu', - family: 'debian', - version: '18.04.2 LTS (Bionic Beaver)', - platform: 'ubuntu', - }, - ip: ['10.128.0.4', 'fe80::4001:aff:fe80:4'], - containerized: false, - name: 'suricata-iowa', - id: 'be1f3d767896212736b880e846876dcb', - mac: ['42:01:0a:80:00:04'], - architecture: 'x86_64', - }, -}; - -export const formattedEmptySource = { - source: { - firstSeen: null, - lastSeen: null, - autonomousSystem: {}, - geo: {}, - }, -}; - -export const mockUsersData: UsersResponse = { - took: 445, - timed_out: false, - _shards: { - total: 59, - successful: 59, - skipped: 0, - failed: 0, - }, - hits: { - max_score: null, - hits: [], - }, - aggregations: { - user_count: { - value: 3, - }, - users: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: '_apt', - doc_count: 10, - groupName: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'nogroup', - doc_count: 10, - }, - ], - }, - groupId: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: '65534', - doc_count: 10, - }, - ], - }, - id: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: '104', - doc_count: 10, - }, - ], - }, - }, - { - key: 'root', - doc_count: 109, - groupName: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'Debian-exim', - doc_count: 72, - }, - { - key: 'root', - doc_count: 37, - }, - ], - }, - groupId: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: '116', - doc_count: 72, - }, - { - key: '0', - doc_count: 37, - }, - ], - }, - id: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: '0', - doc_count: 109, - }, - ], - }, - }, - { - key: 'systemd-resolve', - doc_count: 4, - groupName: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [], - }, - groupId: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [], - }, - id: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: '102', - doc_count: 4, - }, - ], - }, - }, - ], - }, - }, -}; - -export const mockFormattedUsersEdges: UsersEdges[] = [ - { - node: { - _id: '_apt', - user: { - id: ['104'], - name: '_apt', - groupId: ['65534'], - groupName: ['nogroup'], - count: 10, - }, - }, - cursor: { - value: '_apt', - tiebreaker: null, - }, - }, - { - node: { - _id: 'root', - user: { - id: ['0'], - name: 'root', - groupId: ['116', '0'], - groupName: ['Debian-exim', 'root'], - count: 109, - }, - }, - cursor: { - value: 'root', - tiebreaker: null, - }, - }, - { - node: { - _id: 'systemd-resolve', - user: { - id: ['102'], - name: 'systemd-resolve', - groupId: [], - groupName: [], - count: 4, - }, - }, - cursor: { - value: 'systemd-resolve', - tiebreaker: null, - }, - }, -]; diff --git a/x-pack/plugins/security_solution/server/lib/ip_details/query_overview.dsl.ts b/x-pack/plugins/security_solution/server/lib/ip_details/query_overview.dsl.ts deleted file mode 100644 index d9c8f32d0b465c..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/ip_details/query_overview.dsl.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { isEmpty } from 'lodash/fp'; -import { IpOverviewRequestOptions } from './index'; - -const getAggs = (type: string, ip: string) => { - return { - [type]: { - filter: { - term: { - [`${type}.ip`]: ip, - }, - }, - aggs: { - firstSeen: { - min: { - field: '@timestamp', - }, - }, - lastSeen: { - max: { - field: '@timestamp', - }, - }, - as: { - filter: { - exists: { - field: `${type}.as`, - }, - }, - aggs: { - results: { - top_hits: { - size: 1, - _source: [`${type}.as`], - sort: [ - { - '@timestamp': 'desc', - }, - ], - }, - }, - }, - }, - geo: { - filter: { - exists: { - field: `${type}.geo`, - }, - }, - aggs: { - results: { - top_hits: { - size: 1, - _source: [`${type}.geo`], - sort: [ - { - '@timestamp': 'desc', - }, - ], - }, - }, - }, - }, - }, - }, - }; -}; - -const getHostAggs = (ip: string) => { - return { - host: { - filter: { - term: { - 'host.ip': ip, - }, - }, - aggs: { - results: { - top_hits: { - size: 1, - _source: ['host'], - sort: [ - { - '@timestamp': 'desc', - }, - ], - }, - }, - }, - }, - }; -}; - -export const buildOverviewQuery = ({ - defaultIndex, - docValueFields, - ip, -}: IpOverviewRequestOptions) => { - const dslQuery = { - allowNoIndices: true, - index: defaultIndex, - ignoreUnavailable: true, - body: { - ...(isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), - aggs: { - ...getAggs('source', ip), - ...getAggs('destination', ip), - ...getHostAggs(ip), - }, - query: { - bool: { - should: [], - }, - }, - size: 0, - track_total_hits: false, - }, - }; - - return dslQuery; -}; diff --git a/x-pack/plugins/security_solution/server/lib/ip_details/query_users.dsl.ts b/x-pack/plugins/security_solution/server/lib/ip_details/query_users.dsl.ts deleted file mode 100644 index 293a487777fd21..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/ip_details/query_users.dsl.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { assertUnreachable } from '../../../common/utility_types'; -import { Direction, UsersFields, UsersSortField } from '../../graphql/types'; -import { createQueryFilterClauses } from '../../utils/build_query'; - -import { UsersRequestOptions } from './index'; - -export const buildUsersQuery = ({ - ip, - sort, - filterQuery, - flowTarget, - pagination: { querySize }, - defaultIndex, - sourceConfiguration: { - fields: { timestamp }, - }, - timerange: { from, to }, -}: UsersRequestOptions) => { - const filter = [ - ...createQueryFilterClauses(filterQuery), - { - range: { - [timestamp]: { gte: from, lte: to, format: 'strict_date_optional_time' }, - }, - }, - { term: { [`${flowTarget}.ip`]: ip } }, - ]; - - const dslQuery = { - allowNoIndices: true, - index: defaultIndex, - ignoreUnavailable: true, - body: { - aggs: { - user_count: { - cardinality: { - field: 'user.name', - }, - }, - users: { - terms: { - field: 'user.name', - size: querySize, - order: { - ...getQueryOrder(sort), - }, - }, - aggs: { - id: { - terms: { - field: 'user.id', - }, - }, - groupId: { - terms: { - field: 'user.group.id', - }, - }, - groupName: { - terms: { - field: 'user.group.name', - }, - }, - }, - }, - }, - query: { - bool: { - filter, - must_not: [ - { - term: { - 'event.category': 'authentication', - }, - }, - ], - }, - }, - size: 0, - track_total_hits: false, - }, - }; - - return dslQuery; -}; - -type QueryOrder = { _count: Direction } | { _key: Direction }; - -const getQueryOrder = (sort: UsersSortField): QueryOrder => { - switch (sort.field) { - case UsersFields.name: - return { _key: sort.direction }; - case UsersFields.count: - return { _count: sort.direction }; - default: - return assertUnreachable(sort.field); - } -}; diff --git a/x-pack/plugins/security_solution/server/lib/ip_details/types.ts b/x-pack/plugins/security_solution/server/lib/ip_details/types.ts deleted file mode 100644 index d137d919932f73..00000000000000 --- a/x-pack/plugins/security_solution/server/lib/ip_details/types.ts +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { IpOverviewData, UsersData } from '../../graphql/types'; -import { FrameworkRequest, RequestBasicOptions } from '../framework'; -import { Hit, ShardsResponse, TotalValue } from '../types'; - -export interface IpDetailsAdapter { - getIpDetails(request: FrameworkRequest, options: RequestBasicOptions): Promise; - getUsers(request: FrameworkRequest, options: RequestBasicOptions): Promise; -} - -interface ResultHit { - doc_count: number; - results: { - hits: { - total: TotalValue | number; - max_score: number | null; - hits: Array<{ - _source: T; - sort?: [number]; - _index?: string; - _type?: string; - _id?: string; - _score?: number | null; - }>; - }; - }; -} - -export interface OverviewHit { - took?: number; - timed_out?: boolean; - _scroll_id?: string; - _shards?: ShardsResponse; - timeout?: number; - hits?: { - total: number; - hits: Hit[]; - }; - doc_count: number; - geo: ResultHit; - autonomousSystem: ResultHit; - firstSeen: { - value: number; - value_as_string: string; - }; - lastSeen: { - value: number; - value_as_string: string; - }; -} - -export type OverviewHostHit = ResultHit; - -export interface IpOverviewHit { - aggregations: { - destination?: OverviewHit; - source?: OverviewHit; - host: ResultHit; - }; - _shards: { - total: number; - successful: number; - skipped: number; - failed: number; - }; - hits: { - total: { - value: number; - relation: string; - }; - max_score: number | null; - hits: []; - }; - took: number; - timeout: number; -} - -// Users Table - -export interface UsersResponse { - took: number; - timed_out: boolean; - _shards: UsersShards; - hits: UsersHits; - aggregations: Aggregations; -} -interface UsersShards { - total: number; - successful: number; - skipped: number; - failed: number; -} -interface UsersHits { - max_score: null; - hits: string[]; -} -interface Aggregations { - user_count: UserCount; - users: Users; -} -interface UserCount { - value: number; -} -interface Users { - doc_count_error_upper_bound: number; - sum_other_doc_count: number; - buckets: UsersBucketsItem[]; -} -export interface UsersBucketsItem { - key: string; - doc_count: number; - groupName?: UsersGroupName; - groupId?: UsersGroupId; - id?: Id; -} -export interface UsersGroupName { - doc_count_error_upper_bound: number; - sum_other_doc_count: number; - buckets: UsersBucketsItem[]; -} -export interface UsersGroupId { - doc_count_error_upper_bound: number; - sum_other_doc_count: number; - buckets: UsersBucketsItem[]; -} -interface Id { - doc_count_error_upper_bound: number; - sum_other_doc_count: number; - buckets: UsersBucketsItem[]; -} diff --git a/x-pack/plugins/security_solution/server/lib/types.ts b/x-pack/plugins/security_solution/server/lib/types.ts index 3c7c1cd3d7cff9..6e233f6e49d3b5 100644 --- a/x-pack/plugins/security_solution/server/lib/types.ts +++ b/x-pack/plugins/security_solution/server/lib/types.ts @@ -13,7 +13,6 @@ import { Events } from './events'; import { FrameworkAdapter, FrameworkRequest } from './framework'; import { Hosts } from './hosts'; import { IndexFields } from './index_fields'; -import { IpDetails } from './ip_details'; import { KpiHosts } from './kpi_hosts'; import { KpiNetwork } from './kpi_network'; import { Network } from './network'; @@ -31,7 +30,6 @@ export interface AppDomainLibs { events: Events; fields: IndexFields; hosts: Hosts; - ipDetails: IpDetails; matrixHistogram: MatrixHistogram; network: Network; kpiNetwork: KpiNetwork; diff --git a/x-pack/test/api_integration/apis/security_solution/index.js b/x-pack/test/api_integration/apis/security_solution/index.js index a9ddf091245f79..a143d94dde1726 100644 --- a/x-pack/test/api_integration/apis/security_solution/index.js +++ b/x-pack/test/api_integration/apis/security_solution/index.js @@ -21,7 +21,7 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./timeline')); loadTestFile(require.resolve('./timeline_details')); // loadTestFile(require.resolve('./uncommon_processes')); - loadTestFile(require.resolve('./users')); + // loadTestFile(require.resolve('./users')); // loadTestFile(require.resolve('./tls')); loadTestFile(require.resolve('./feature_controls')); }); diff --git a/x-pack/test/api_integration/apis/security_solution/network_details.ts b/x-pack/test/api_integration/apis/security_solution/network_details.ts index cffcd790fa19cf..7b851e875454de 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_details.ts @@ -5,7 +5,9 @@ */ import expect from '@kbn/expect'; +// @ts-expect-error import { ipOverviewQuery } from '../../../../plugins/security_solution/public/network/containers/details/index.gql_query'; +// @ts-expect-error import { GetIpOverviewQuery } from '../../../../plugins/security_solution/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; diff --git a/x-pack/test/api_integration/apis/security_solution/users.ts b/x-pack/test/api_integration/apis/security_solution/users.ts index abb2c5b2f5bbdd..9d42fc0b9788ba 100644 --- a/x-pack/test/api_integration/apis/security_solution/users.ts +++ b/x-pack/test/api_integration/apis/security_solution/users.ts @@ -5,11 +5,14 @@ */ import expect from '@kbn/expect'; +// @ts-expect-error import { usersQuery } from '../../../../plugins/security_solution/public/network/containers/users/index.gql_query'; import { Direction, + // @ts-expect-error UsersFields, FlowTarget, + // @ts-expect-error GetUsersQuery, } from '../../../../plugins/security_solution/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; From 839817ca9a7a359ec17ab067cc360c3cff30e3e5 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 24 Sep 2020 09:55:32 +0200 Subject: [PATCH 35/35] [Lens] show meta field data in Lens (#77210) --- .../indexpattern_datasource/datapanel.scss | 21 -- .../datapanel.test.tsx | 35 +- .../indexpattern_datasource/datapanel.tsx | 318 ++++++------------ .../dimension_panel/field_select.tsx | 11 +- .../indexpattern_datasource/field_item.tsx | 5 +- .../indexpattern_datasource/field_list.scss | 20 ++ .../indexpattern_datasource/field_list.tsx | 193 +++++++++++ .../fields_accordion.test.tsx | 10 +- .../fields_accordion.tsx | 4 +- .../indexpattern_datasource/loader.test.ts | 52 ++- .../public/indexpattern_datasource/loader.ts | 1 + .../public/indexpattern_datasource/types.ts | 1 + .../server/routes/existing_fields.test.ts | 32 +- .../lens/server/routes/existing_fields.ts | 32 +- .../apis/lens/existing_fields.ts | 9 + 15 files changed, 493 insertions(+), 251 deletions(-) create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/field_list.scss create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/field_list.tsx diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss index 70fb57ee79ee55..155b954e9cf170 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss @@ -10,27 +10,6 @@ margin-bottom: $euiSizeS; } -/** - * 1. Don't cut off the shadow of the field items - */ - -.lnsInnerIndexPatternDataPanel__listWrapper { - @include euiOverflowShadow; - @include euiScrollBar; - margin-left: -$euiSize; /* 1 */ - position: relative; - flex-grow: 1; - overflow: auto; -} - -.lnsInnerIndexPatternDataPanel__list { - padding-top: $euiSizeS; - position: absolute; - top: 0; - left: $euiSize; /* 1 */ - right: $euiSizeXS; /* 1 */ -} - .lnsInnerIndexPatternDataPanel__fieldItems { // Quick fix for making sure the shadow and focus rings are visible outside the accordion bounds padding: $euiSizeXS $euiSizeXS 0; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx index f17bf172b0fb18..7fb64d1613d324 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx @@ -623,11 +623,40 @@ describe('IndexPattern Data Panel', () => { ).toEqual(['client', 'source', 'timestampLabel']); }); + it('should show meta fields accordion', async () => { + const wrapper = mountWithIntl( + + ); + wrapper + .find('[data-test-subj="lnsIndexPatternMetaFields"]') + .find('button') + .first() + .simulate('click'); + expect( + wrapper + .find('[data-test-subj="lnsIndexPatternMetaFields"]') + .find(FieldItem) + .first() + .prop('field').name + ).toEqual('_id'); + }); + it('should display NoFieldsCallout when all fields are empty', async () => { const wrapper = mountWithIntl( ); - expect(wrapper.find(NoFieldsCallout).length).toEqual(1); + expect(wrapper.find(NoFieldsCallout).length).toEqual(2); expect( wrapper .find('[data-test-subj="lnsIndexPatternAvailableFields"]') @@ -654,7 +683,7 @@ describe('IndexPattern Data Panel', () => { .length ).toEqual(1); wrapper.setProps({ existingFields: { idx1: {} } }); - expect(wrapper.find(NoFieldsCallout).length).toEqual(1); + expect(wrapper.find(NoFieldsCallout).length).toEqual(2); }); it('should filter down by name', () => { @@ -699,7 +728,7 @@ describe('IndexPattern Data Panel', () => { expect(wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name)).toEqual([ 'Records', ]); - expect(wrapper.find(NoFieldsCallout).length).toEqual(2); + expect(wrapper.find(NoFieldsCallout).length).toEqual(3); }); it('should toggle type if clicked again', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx index f7adf91e307da4..4e85cb5b5d46c3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx @@ -5,14 +5,13 @@ */ import './datapanel.scss'; -import { uniq, keyBy, groupBy, throttle } from 'lodash'; -import React, { useState, useEffect, memo, useCallback, useMemo } from 'react'; +import { uniq, keyBy, groupBy } from 'lodash'; +import React, { useState, memo, useCallback, useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiContextMenuPanel, EuiContextMenuItem, - EuiContextMenuPanelProps, EuiPopover, EuiCallOut, EuiFormControlLayout, @@ -25,8 +24,6 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { DataPublicPluginStart, EsQueryConfig, Query, Filter } from 'src/plugins/data/public'; import { DatasourceDataPanelProps, DataType, StateSetter } from '../types'; import { ChildDragDropProvider, DragContextState } from '../drag_drop'; -import { FieldItem } from './field_item'; -import { NoFieldsCallout } from './no_fields_callout'; import { IndexPattern, IndexPatternPrivateState, @@ -37,7 +34,6 @@ import { trackUiEvent } from '../lens_ui_telemetry'; import { syncExistingFields } from './loader'; import { fieldExists } from './pure_helpers'; import { Loader } from '../loader'; -import { FieldsAccordion } from './fields_accordion'; import { esQuery, IIndexPattern } from '../../../../../src/plugins/data/public'; export type Props = DatasourceDataPanelProps & { @@ -52,18 +48,13 @@ export type Props = DatasourceDataPanelProps & { import { LensFieldIcon } from './lens_field_icon'; import { ChangeIndexPattern } from './change_indexpattern'; import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; - -// TODO the typings for EuiContextMenuPanel are incorrect - watchedItemProps is missing. This can be removed when the types are adjusted -const FixedEuiContextMenuPanel = (EuiContextMenuPanel as unknown) as React.FunctionComponent< - EuiContextMenuPanelProps & { watchedItemProps: string[] } ->; +import { FieldGroups, FieldList } from './field_list'; function sortFields(fieldA: IndexPatternField, fieldB: IndexPatternField) { return fieldA.displayName.localeCompare(fieldB.displayName, undefined, { sensitivity: 'base' }); } const supportedFieldTypes = new Set(['string', 'number', 'boolean', 'date', 'ip', 'document']); -const PAGINATION_SIZE = 50; const fieldTypeNames: Record = { document: i18n.translate('xpack.lens.datatypes.record', { defaultMessage: 'record' }), @@ -212,18 +203,19 @@ interface DataPanelState { isTypeFilterOpen: boolean; isAvailableAccordionOpen: boolean; isEmptyAccordionOpen: boolean; + isMetaAccordionOpen: boolean; } -export interface FieldsGroup { +const defaultFieldGroups: { specialFields: IndexPatternField[]; availableFields: IndexPatternField[]; emptyFields: IndexPatternField[]; -} - -const defaultFieldGroups = { + metaFields: IndexPatternField[]; +} = { specialFields: [], availableFields: [], emptyFields: [], + metaFields: [], }; const fieldFiltersLabel = i18n.translate('xpack.lens.indexPatterns.fieldFiltersLabel', { @@ -261,9 +253,8 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ isTypeFilterOpen: false, isAvailableAccordionOpen: true, isEmptyAccordionOpen: false, + isMetaAccordionOpen: false, }); - const [pageSize, setPageSize] = useState(PAGINATION_SIZE); - const [scrollContainer, setScrollContainer] = useState(undefined); const currentIndexPattern = indexPatterns[currentIndexPatternId]; const allFields = currentIndexPattern.fields; const clearLocalState = () => setLocalState((s) => ({ ...s, nameFilter: '', typeFilter: [] })); @@ -272,17 +263,11 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ (type) => type in fieldTypeNames ); - useEffect(() => { - // Reset the scroll if we have made material changes to the field list - if (scrollContainer) { - scrollContainer.scrollTop = 0; - setPageSize(PAGINATION_SIZE); - } - }, [localState.nameFilter, localState.typeFilter, currentIndexPatternId, scrollContainer]); + const fieldInfoUnavailable = existenceFetchFailed || currentIndexPattern.hasRestrictions; - const fieldGroups: FieldsGroup = useMemo(() => { + const unfilteredFieldGroups: FieldGroups = useMemo(() => { + const fieldByName = keyBy(allFields, 'name'); const containsData = (field: IndexPatternField) => { - const fieldByName = keyBy(allFields, 'name'); const overallField = fieldByName[field.name]; return ( @@ -294,32 +279,105 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ supportedFieldTypes.has(field.type) ); const sorted = allSupportedTypesFields.sort(sortFields); + let groupedFields; // optimization before existingFields are synced if (!hasSyncedExistingFields) { - return { + groupedFields = { ...defaultFieldGroups, ...groupBy(sorted, (field) => { if (field.type === 'document') { return 'specialFields'; + } else if (field.meta) { + return 'metaFields'; } else { return 'emptyFields'; } }), }; } - return { + groupedFields = { ...defaultFieldGroups, ...groupBy(sorted, (field) => { if (field.type === 'document') { return 'specialFields'; + } else if (field.meta) { + return 'metaFields'; } else if (containsData(field)) { return 'availableFields'; } else return 'emptyFields'; }), }; - }, [allFields, existingFields, currentIndexPattern, hasSyncedExistingFields]); - const filteredFieldGroups: FieldsGroup = useMemo(() => { + const fieldGroupDefinitions: FieldGroups = { + SpecialFields: { + fields: groupedFields.specialFields, + fieldCount: 1, + isAffectedByGlobalFilter: false, + isAffectedByTimeFilter: false, + isInitiallyOpen: false, + showInAccordion: false, + title: '', + hideDetails: true, + }, + AvailableFields: { + fields: groupedFields.availableFields, + fieldCount: groupedFields.availableFields.length, + isInitiallyOpen: true, + showInAccordion: true, + title: fieldInfoUnavailable + ? i18n.translate('xpack.lens.indexPattern.allFieldsLabel', { + defaultMessage: 'All fields', + }) + : i18n.translate('xpack.lens.indexPattern.availableFieldsLabel', { + defaultMessage: 'Available fields', + }), + + isAffectedByGlobalFilter: !!filters.length, + isAffectedByTimeFilter: true, + hideDetails: fieldInfoUnavailable, + }, + EmptyFields: { + fields: groupedFields.emptyFields, + fieldCount: groupedFields.emptyFields.length, + isAffectedByGlobalFilter: false, + isAffectedByTimeFilter: false, + isInitiallyOpen: false, + showInAccordion: true, + hideDetails: false, + title: i18n.translate('xpack.lens.indexPattern.emptyFieldsLabel', { + defaultMessage: 'Empty fields', + }), + }, + MetaFields: { + fields: groupedFields.metaFields, + fieldCount: groupedFields.metaFields.length, + isAffectedByGlobalFilter: false, + isAffectedByTimeFilter: false, + isInitiallyOpen: false, + showInAccordion: true, + hideDetails: false, + title: i18n.translate('xpack.lens.indexPattern.metaFieldsLabel', { + defaultMessage: 'Meta fields', + }), + }, + }; + + // do not show empty field accordion if there is no existence information + if (fieldInfoUnavailable) { + delete fieldGroupDefinitions.EmptyFields; + } + + return fieldGroupDefinitions; + }, [ + allFields, + existingFields, + currentIndexPattern, + hasSyncedExistingFields, + fieldInfoUnavailable, + filters.length, + ]); + + const fieldGroups: FieldGroups = useMemo(() => { const filterFieldGroup = (fieldGroup: IndexPatternField[]) => fieldGroup.filter((field) => { if ( @@ -329,76 +387,18 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ ) { return false; } - if (localState.typeFilter.length > 0) { return localState.typeFilter.includes(field.type as DataType); } return true; }); - - return Object.entries(fieldGroups).reduce((acc, [name, fields]) => { - return { - ...acc, - [name]: filterFieldGroup(fields), - }; - }, defaultFieldGroups); - }, [fieldGroups, localState.nameFilter, localState.typeFilter]); - - const lazyScroll = useCallback(() => { - if (scrollContainer) { - const nearBottom = - scrollContainer.scrollTop + scrollContainer.clientHeight > - scrollContainer.scrollHeight * 0.9; - if (nearBottom) { - const displayedFieldsLength = - (localState.isAvailableAccordionOpen ? filteredFieldGroups.availableFields.length : 0) + - (localState.isEmptyAccordionOpen ? filteredFieldGroups.emptyFields.length : 0); - setPageSize( - Math.max( - PAGINATION_SIZE, - Math.min(pageSize + PAGINATION_SIZE * 0.5, displayedFieldsLength) - ) - ); - } - } - }, [ - scrollContainer, - localState.isAvailableAccordionOpen, - localState.isEmptyAccordionOpen, - filteredFieldGroups, - pageSize, - setPageSize, - ]); - - const [paginatedAvailableFields, paginatedEmptyFields]: [ - IndexPatternField[], - IndexPatternField[] - ] = useMemo(() => { - const { availableFields, emptyFields } = filteredFieldGroups; - const isAvailableAccordionOpen = localState.isAvailableAccordionOpen; - const isEmptyAccordionOpen = localState.isEmptyAccordionOpen; - - if (isAvailableAccordionOpen && isEmptyAccordionOpen) { - if (availableFields.length > pageSize) { - return [availableFields.slice(0, pageSize), []]; - } else { - return [availableFields, emptyFields.slice(0, pageSize - availableFields.length)]; - } - } - if (isAvailableAccordionOpen && !isEmptyAccordionOpen) { - return [availableFields.slice(0, pageSize), []]; - } - - if (!isAvailableAccordionOpen && isEmptyAccordionOpen) { - return [[], emptyFields.slice(0, pageSize)]; - } - return [[], []]; - }, [ - localState.isAvailableAccordionOpen, - localState.isEmptyAccordionOpen, - filteredFieldGroups, - pageSize, - ]); + return Object.fromEntries( + Object.entries(unfilteredFieldGroups).map(([name, group]) => [ + name, + { ...group, fields: filterFieldGroup(group.fields) }, + ]) + ); + }, [unfilteredFieldGroups, localState.nameFilter, localState.typeFilter]); const fieldProps = useMemo( () => ({ @@ -423,8 +423,6 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ ] ); - const fieldInfoUnavailable = existenceFetchFailed || currentIndexPattern.hasRestrictions; - return ( } > - ( @@ -545,115 +543,21 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ -
    { - if (el && !el.dataset.dynamicScroll) { - el.dataset.dynamicScroll = 'true'; - setScrollContainer(el); - } + + field.type === 'document' || + fieldExists(existingFields, currentIndexPattern.title, field.name) + } + fieldProps={fieldProps} + fieldGroups={fieldGroups} + hasSyncedExistingFields={!!hasSyncedExistingFields} + filter={{ + nameFilter: localState.nameFilter, + typeFilter: localState.typeFilter, }} - onScroll={throttle(lazyScroll, 100)} - > -
    - {filteredFieldGroups.specialFields.map((field: IndexPatternField) => ( - - ))} - - - { - setLocalState((s) => ({ - ...s, - isAvailableAccordionOpen: open, - })); - const displayedFieldLength = - (open ? filteredFieldGroups.availableFields.length : 0) + - (localState.isEmptyAccordionOpen ? filteredFieldGroups.emptyFields.length : 0); - setPageSize( - Math.max(PAGINATION_SIZE, Math.min(pageSize * 1.5, displayedFieldLength)) - ); - }} - showExistenceFetchError={existenceFetchFailed} - renderCallout={ - - } - /> - - {!fieldInfoUnavailable && ( - { - setLocalState((s) => ({ - ...s, - isEmptyAccordionOpen: open, - })); - const displayedFieldLength = - (localState.isAvailableAccordionOpen - ? filteredFieldGroups.availableFields.length - : 0) + (open ? filteredFieldGroups.emptyFields.length : 0); - setPageSize( - Math.max(PAGINATION_SIZE, Math.min(pageSize * 1.5, displayedFieldLength)) - ); - }} - renderCallout={ - - } - /> - )} - -
    -
    + currentIndexPatternId={currentIndexPatternId} + existenceFetchFailed={existenceFetchFailed} + />
    diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx index 60f60d7cb80c11..e71a85868b8552 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx @@ -116,7 +116,8 @@ export function FieldSelect({ })); } - const [availableFields, emptyFields] = _.partition(normalFields, containsData); + const [metaFields, nonMetaFields] = _.partition(normalFields, (field) => fieldMap[field].meta); + const [availableFields, emptyFields] = _.partition(nonMetaFields, containsData); const constructFieldsOptions = (fieldsArr: string[], label: string) => fieldsArr.length > 0 && { @@ -138,10 +139,18 @@ export function FieldSelect({ }) ); + const metaFieldsOptions = constructFieldsOptions( + metaFields, + i18n.translate('xpack.lens.indexPattern.metaFieldsLabel', { + defaultMessage: 'Meta fields', + }) + ); + return [ ...fieldNamesToOptions(specialFields), availableFieldsOptions, emptyFieldsOptions, + metaFieldsOptions, ].filter(Boolean); }, [ incompatibleSelectedOperationType, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index 1f6d7911b3a33c..1eeb64127310f9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -184,7 +184,8 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { defaultMessage: 'Click for a field preview, or drag and drop to visualize.', }) : i18n.translate('xpack.lens.indexPattern.fieldStatsButtonEmptyLabel', { - defaultMessage: "This field doesn't have data. Drag and drop to visualize.", + defaultMessage: + 'This field doesn’t have any data but you can still drag and drop to visualize.', }) } type="iInCircle" @@ -307,7 +308,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) { {i18n.translate('xpack.lens.indexPattern.fieldStatsNoData', { defaultMessage: - 'This field is empty because it doesn’t exist in the 500 sampled documents.', + 'This field is empty because it doesn’t exist in the 500 sampled documents. Adding this field to the configuration may result in a blank chart.', })} ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_list.scss b/x-pack/plugins/lens/public/indexpattern_datasource/field_list.scss new file mode 100644 index 00000000000000..f28581b835b078 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_list.scss @@ -0,0 +1,20 @@ +/** + * 1. Don't cut off the shadow of the field items + */ + +.lnsIndexPatternFieldList { + @include euiOverflowShadow; + @include euiScrollBar; + margin-left: -$euiSize; /* 1 */ + position: relative; + flex-grow: 1; + overflow: auto; +} + +.lnsIndexPatternFieldList__accordionContainer { + padding-top: $euiSizeS; + position: absolute; + top: 0; + left: $euiSize; /* 1 */ + right: $euiSizeXS; /* 1 */ +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_list.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_list.tsx new file mode 100644 index 00000000000000..4a9b3a0c63e3f4 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_list.tsx @@ -0,0 +1,193 @@ +/* + * 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 './field_list.scss'; +import { throttle } from 'lodash'; +import React, { useState, Fragment, useCallback, useMemo, useEffect } from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import { FieldItem } from './field_item'; +import { NoFieldsCallout } from './no_fields_callout'; +import { IndexPatternField } from './types'; +import { FieldItemSharedProps, FieldsAccordion } from './fields_accordion'; +const PAGINATION_SIZE = 50; + +export interface FieldsGroup { + specialFields: IndexPatternField[]; + availableFields: IndexPatternField[]; + emptyFields: IndexPatternField[]; + metaFields: IndexPatternField[]; +} + +export type FieldGroups = Record< + string, + { + fields: IndexPatternField[]; + fieldCount: number; + showInAccordion: boolean; + isInitiallyOpen: boolean; + title: string; + isAffectedByGlobalFilter: boolean; + isAffectedByTimeFilter: boolean; + hideDetails?: boolean; + } +>; + +function getDisplayedFieldsLength( + fieldGroups: FieldGroups, + accordionState: Partial> +) { + return Object.entries(fieldGroups) + .filter(([key]) => accordionState[key]) + .reduce((allFieldCount, [, { fields }]) => allFieldCount + fields.length, 0); +} + +export function FieldList({ + exists, + fieldGroups, + existenceFetchFailed, + fieldProps, + hasSyncedExistingFields, + filter, + currentIndexPatternId, +}: { + exists: (field: IndexPatternField) => boolean; + fieldGroups: FieldGroups; + fieldProps: FieldItemSharedProps; + hasSyncedExistingFields: boolean; + existenceFetchFailed?: boolean; + filter: { + nameFilter: string; + typeFilter: string[]; + }; + currentIndexPatternId: string; +}) { + const [pageSize, setPageSize] = useState(PAGINATION_SIZE); + const [scrollContainer, setScrollContainer] = useState(undefined); + const [accordionState, setAccordionState] = useState>>(() => + Object.fromEntries( + Object.entries(fieldGroups) + .filter(([, { showInAccordion }]) => showInAccordion) + .map(([key, { isInitiallyOpen }]) => [key, isInitiallyOpen]) + ) + ); + + const isAffectedByFieldFilter = !!(filter.typeFilter.length || filter.nameFilter.length); + + useEffect(() => { + // Reset the scroll if we have made material changes to the field list + if (scrollContainer) { + scrollContainer.scrollTop = 0; + setPageSize(PAGINATION_SIZE); + } + }, [filter.nameFilter, filter.typeFilter, currentIndexPatternId, scrollContainer]); + + const lazyScroll = useCallback(() => { + if (scrollContainer) { + const nearBottom = + scrollContainer.scrollTop + scrollContainer.clientHeight > + scrollContainer.scrollHeight * 0.9; + if (nearBottom) { + setPageSize( + Math.max( + PAGINATION_SIZE, + Math.min( + pageSize + PAGINATION_SIZE * 0.5, + getDisplayedFieldsLength(fieldGroups, accordionState) + ) + ) + ); + } + } + }, [scrollContainer, pageSize, setPageSize, fieldGroups, accordionState]); + + const paginatedFields = useMemo(() => { + let remainingItems = pageSize; + return Object.fromEntries( + Object.entries(fieldGroups) + .filter(([, { showInAccordion }]) => showInAccordion) + .map(([key, fieldGroup]) => { + if (!accordionState[key] || remainingItems <= 0) { + return [key, []]; + } + const slicedFieldList = fieldGroup.fields.slice(0, remainingItems); + remainingItems = remainingItems - slicedFieldList.length; + return [key, slicedFieldList]; + }) + ); + }, [pageSize, fieldGroups, accordionState]); + + return ( +
    { + if (el && !el.dataset.dynamicScroll) { + el.dataset.dynamicScroll = 'true'; + setScrollContainer(el); + } + }} + onScroll={throttle(lazyScroll, 100)} + > +
    + {Object.entries(fieldGroups) + .filter(([, { showInAccordion }]) => !showInAccordion) + .flatMap(([, { fields }]) => + fields.map((field) => ( + + )) + )} + + {Object.entries(fieldGroups) + .filter(([, { showInAccordion }]) => showInAccordion) + .map(([key, fieldGroup]) => ( + + { + setAccordionState((s) => ({ + ...s, + [key]: open, + })); + const displayedFieldLength = getDisplayedFieldsLength(fieldGroups, { + ...accordionState, + [key]: open, + }); + setPageSize( + Math.max(PAGINATION_SIZE, Math.min(pageSize * 1.5, displayedFieldLength)) + ); + }} + showExistenceFetchError={existenceFetchFailed} + renderCallout={ + + } + /> + + + ))} +
    +
    + ); +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.test.tsx index b0604efff7b891..7d1c80e5a7f6a9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.test.tsx @@ -71,11 +71,19 @@ describe('Fields Accordion', () => { paginatedFields: indexPattern.fields, fieldProps, renderCallout:
    Callout
    , - exists: true, + exists: () => true, }; }); it('renders correct number of Field Items', () => { + const wrapper = mountWithIntl( + field.name === 'timestamp'} /> + ); + expect(wrapper.find(FieldItem).at(0).prop('exists')).toEqual(true); + expect(wrapper.find(FieldItem).at(1).prop('exists')).toEqual(false); + }); + + it('passed correct exists flag to each field', () => { const wrapper = mountWithIntl(); expect(wrapper.find(FieldItem).length).toEqual(2); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx index 30a92c21ff6610..e531eb72f94caa 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx @@ -45,7 +45,7 @@ export interface FieldsAccordionProps { paginatedFields: IndexPatternField[]; fieldProps: FieldItemSharedProps; renderCallout: JSX.Element; - exists: boolean; + exists: (field: IndexPatternField) => boolean; showExistenceFetchError?: boolean; hideDetails?: boolean; } @@ -71,7 +71,7 @@ export const InnerFieldsAccordion = function InnerFieldsAccordion({ {...fieldProps} key={field.name} field={field} - exists={exists} + exists={exists(field)} hideDetails={hideDetails} /> ), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts index 19213d4afc9bcb..ef6abbec9a34d1 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts @@ -197,7 +197,7 @@ function mockClient() { function mockIndexPatternsService() { return ({ get: jest.fn(async (id: '1' | '2') => { - return sampleIndexPatternsFromService[id]; + return { ...sampleIndexPatternsFromService[id], metaFields: [] }; }), } as unknown) as Pick; } @@ -248,6 +248,7 @@ describe('loader', () => { get: jest.fn(async () => ({ id: 'foo', title: 'Foo index', + metaFields: [], typeMeta: { aggs: { date_histogram: { @@ -295,6 +296,55 @@ describe('loader', () => { date_histogram: { agg: 'date_histogram', fixed_interval: 'm' }, }); }); + + it('should map meta flag', async () => { + const cache = await loadIndexPatterns({ + cache: {}, + patterns: ['foo'], + indexPatternsService: ({ + get: jest.fn(async () => ({ + id: 'foo', + title: 'Foo index', + metaFields: ['timestamp'], + typeMeta: { + aggs: { + date_histogram: { + timestamp: { + agg: 'date_histogram', + fixed_interval: 'm', + }, + }, + sum: { + bytes: { + agg: 'sum', + }, + }, + }, + }, + fields: [ + { + name: 'timestamp', + displayName: 'timestampLabel', + type: 'date', + aggregatable: true, + searchable: true, + }, + { + name: 'bytes', + displayName: 'bytes', + type: 'number', + aggregatable: true, + searchable: true, + }, + ], + })), + } as unknown) as Pick, + }); + + expect(cache.foo.fields.find((f: IndexPatternField) => f.name === 'timestamp')!.meta).toEqual( + true + ); + }); }); describe('loadInitialState', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index 0ab658b9613363..c4b1eb9e0c4c4a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -63,6 +63,7 @@ export async function loadIndexPatterns({ type: field.type, aggregatable: field.aggregatable, searchable: field.searchable, + meta: indexPattern.metaFields.includes(field.name), esTypes: field.esTypes, scripted: field.scripted, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts index b691c5b5c4c403..a3c0e8aed74219 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts @@ -26,6 +26,7 @@ export interface IndexPattern { export type IndexPatternField = IFieldType & { displayName: string; aggregationRestrictions?: Partial; + meta?: boolean; }; export interface IndexPatternLayer { diff --git a/x-pack/plugins/lens/server/routes/existing_fields.test.ts b/x-pack/plugins/lens/server/routes/existing_fields.test.ts index 728b78c8e97bc4..9799dcf92ae411 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.test.ts +++ b/x-pack/plugins/lens/server/routes/existing_fields.test.ts @@ -15,6 +15,7 @@ describe('existingFields', () => { name, isScript: false, isAlias: false, + isMeta: false, path: name.split('.'), ...obj, }; @@ -101,6 +102,15 @@ describe('existingFields', () => { expect(result).toEqual(['baz']); }); + + it('supports meta fields', () => { + const result = existingFields( + [{ _mymeta: 'abc', ...indexPattern({}, { bar: 'scriptvalue' }) }], + [field({ name: '_mymeta', isMeta: true, path: ['_mymeta'] })] + ); + + expect(result).toEqual(['_mymeta']); + }); }); describe('buildFieldList', () => { @@ -116,6 +126,7 @@ describe('buildFieldList', () => { { name: 'bar' }, { name: '@bar' }, { name: 'baz' }, + { name: '_mymeta' }, ]), }, references: [], @@ -142,7 +153,7 @@ describe('buildFieldList', () => { ]; it('uses field descriptors to determine the path', () => { - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors); + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, []); expect(fields.find((f) => f.name === 'baz')).toMatchObject({ isAlias: false, isScript: false, @@ -152,7 +163,7 @@ describe('buildFieldList', () => { }); it('uses aliases to determine the path', () => { - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors); + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, []); expect(fields.find((f) => f.isAlias)).toMatchObject({ isAlias: true, isScript: false, @@ -162,7 +173,7 @@ describe('buildFieldList', () => { }); it('supports scripted fields', () => { - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors); + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, []); expect(fields.find((f) => f.isScript)).toMatchObject({ isAlias: false, isScript: true, @@ -173,13 +184,24 @@ describe('buildFieldList', () => { }); }); + it('supports meta fields', () => { + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, ['_mymeta']); + expect(fields.find((f) => f.isMeta)).toMatchObject({ + isAlias: false, + isScript: false, + isMeta: true, + name: '_mymeta', + path: ['_mymeta'], + }); + }); + it('handles missing mappings', () => { - const fields = buildFieldList(indexPattern, {}, fieldDescriptors); + const fields = buildFieldList(indexPattern, {}, fieldDescriptors, []); expect(fields.every((f) => f.isAlias === false)).toEqual(true); }); it('handles empty fieldDescriptors by skipping multi-mappings', () => { - const fields = buildFieldList(indexPattern, mappings, []); + const fields = buildFieldList(indexPattern, mappings, [], []); expect(fields.find((f) => f.name === 'baz')).toMatchObject({ isAlias: false, isScript: false, diff --git a/x-pack/plugins/lens/server/routes/existing_fields.ts b/x-pack/plugins/lens/server/routes/existing_fields.ts index 7ab3cdceb21457..33fcafacfad733 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.ts +++ b/x-pack/plugins/lens/server/routes/existing_fields.ts @@ -12,6 +12,7 @@ import { BASE_API_URL } from '../../common'; import { IndexPatternsFetcher, IndexPatternAttributes, + UI_SETTINGS, } from '../../../../../src/plugins/data/server'; /** @@ -36,13 +37,12 @@ export interface Field { name: string; isScript: boolean; isAlias: boolean; + isMeta: boolean; path: string[]; lang?: string; script?: string; } -const metaFields = ['_source', '_type']; - export async function existingFieldsRoute(setup: CoreSetup) { const router = setup.http.createRouter(); @@ -104,14 +104,15 @@ async function fetchFieldExistence({ toDate?: string; timeFieldName?: string; }) { + const metaFields: string[] = await context.core.uiSettings.client.get(UI_SETTINGS.META_FIELDS); const { indexPattern, indexPatternTitle, mappings, fieldDescriptors, - } = await fetchIndexPatternDefinition(indexPatternId, context); + } = await fetchIndexPatternDefinition(indexPatternId, context, metaFields); - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors); + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, metaFields); const docs = await fetchIndexPatternStats({ fromDate, toDate, @@ -128,7 +129,11 @@ async function fetchFieldExistence({ }; } -async function fetchIndexPatternDefinition(indexPatternId: string, context: RequestHandlerContext) { +async function fetchIndexPatternDefinition( + indexPatternId: string, + context: RequestHandlerContext, + metaFields: string[] +) { const savedObjectsClient = context.core.savedObjects.client; const requestClient = context.core.elasticsearch.legacy.client; const indexPattern = await savedObjectsClient.get( @@ -178,7 +183,8 @@ async function fetchIndexPatternDefinition(indexPatternId: string, context: Requ export function buildFieldList( indexPattern: SavedObject, mappings: MappingResult | {}, - fieldDescriptors: FieldDescriptor[] + fieldDescriptors: FieldDescriptor[], + metaFields: string[] ): Field[] { const aliasMap = Object.entries(Object.values(mappings)[0]?.mappings.properties ?? {}) .map(([name, v]) => ({ ...v, name })) @@ -204,6 +210,9 @@ export function buildFieldList( path: path.split('.'), lang: field.lang, script: field.script, + // id is a special case - it doesn't show up in the meta field list, + // but as it's not part of source, it has to be handled separately. + isMeta: metaFields.includes(field.name) || field.name === '_id', }; } ); @@ -312,7 +321,7 @@ function exists(obj: unknown, path: string[], i = 0): boolean { * Exported only for unit tests. */ export function existingFields( - docs: Array<{ _source: unknown; fields: unknown }>, + docs: Array<{ _source: unknown; fields: unknown; [key: string]: unknown }>, fields: Field[] ): string[] { const missingFields = new Set(fields); @@ -323,7 +332,14 @@ export function existingFields( } missingFields.forEach((field) => { - if (exists(field.isScript ? doc.fields : doc._source, field.path)) { + let fieldStore = doc._source; + if (field.isScript) { + fieldStore = doc.fields; + } + if (field.isMeta) { + fieldStore = doc; + } + if (exists(fieldStore, field.path)) { missingFields.delete(field); } }); diff --git a/x-pack/test/api_integration/apis/lens/existing_fields.ts b/x-pack/test/api_integration/apis/lens/existing_fields.ts index 92336f2892f433..10ee7bc9b48ea1 100644 --- a/x-pack/test/api_integration/apis/lens/existing_fields.ts +++ b/x-pack/test/api_integration/apis/lens/existing_fields.ts @@ -20,6 +20,9 @@ const fieldsWithData = [ '@tags', '@tags.raw', '@timestamp', + '_id', + '_index', + '_source', 'agent', 'agent.raw', 'bytes', @@ -96,6 +99,9 @@ const fieldsWithData = [ const metricBeatData = [ '@timestamp', + '_id', + '_index', + '_source', 'agent.ephemeral_id', 'agent.hostname', 'agent.id', @@ -185,6 +191,9 @@ export default ({ getService }: FtrProviderContext) => { '@tags', '@tags.raw', '@timestamp', + '_id', + '_index', + '_source', 'agent', 'agent.raw', 'bytes',